@sagentlab/navarch-runtime 0.1.112 → 0.1.117
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.md +56 -18
- package/bin/muse-mcp-bridge.cjs +3 -0
- package/dist/adapters/index.cjs +6 -1
- package/dist/adapters/muse.cjs +433 -0
- package/dist/agent-auth.cjs +2 -1
- package/dist/cli.cjs +4 -1
- package/dist/config.cjs +9 -1
- package/dist/conversation/capability.cjs +1 -1
- package/dist/conversation/loop.cjs +16 -0
- package/dist/conversation/mirror.cjs +225 -0
- package/dist/conversation/workspace.cjs +11 -4
- package/dist/exit-conditions.cjs +1 -1
- package/dist/muse-mcp-bridge.cjs +167 -0
- package/dist/session.cjs +30 -8
- package/dist/worktree-guard.cjs +137 -3
- package/node_modules/@muse-code/sdk/LICENSE +21 -0
- package/node_modules/@muse-code/sdk/README.md +88 -0
- package/node_modules/@muse-code/sdk/dist/src/connection/connection.d.ts +149 -0
- package/node_modules/@muse-code/sdk/dist/src/connection/connection.js +581 -0
- package/node_modules/@muse-code/sdk/dist/src/connection/spawn.d.ts +188 -0
- package/node_modules/@muse-code/sdk/dist/src/connection/spawn.js +496 -0
- package/node_modules/@muse-code/sdk/dist/src/errors.d.ts +71 -0
- package/node_modules/@muse-code/sdk/dist/src/errors.js +76 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/approval.d.ts +96 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/approval.js +293 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/client.d.ts +171 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/client.js +442 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/discarded.d.ts +36 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/discarded.js +36 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/gap-fill.d.ts +80 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/gap-fill.js +388 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/host-death.d.ts +139 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/host-death.js +96 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/host-start.d.ts +62 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/host-start.js +81 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/session.d.ts +309 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/session.js +654 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/turn-handle.d.ts +156 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/turn-handle.js +271 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/turn-submit.d.ts +72 -0
- package/node_modules/@muse-code/sdk/dist/src/facade/turn-submit.js +183 -0
- package/node_modules/@muse-code/sdk/dist/src/fingerprint.d.ts +37 -0
- package/node_modules/@muse-code/sdk/dist/src/fingerprint.js +54 -0
- package/node_modules/@muse-code/sdk/dist/src/fold/item-store.d.ts +110 -0
- package/node_modules/@muse-code/sdk/dist/src/fold/item-store.js +169 -0
- package/node_modules/@muse-code/sdk/dist/src/fold/session-fold.d.ts +536 -0
- package/node_modules/@muse-code/sdk/dist/src/fold/session-fold.js +427 -0
- package/node_modules/@muse-code/sdk/dist/src/fold/state-store.d.ts +68 -0
- package/node_modules/@muse-code/sdk/dist/src/fold/state-store.js +89 -0
- package/node_modules/@muse-code/sdk/dist/src/index.d.ts +65 -0
- package/node_modules/@muse-code/sdk/dist/src/index.js +57 -0
- package/node_modules/@muse-code/sdk/dist/src/msp.d.ts +3380 -0
- package/node_modules/@muse-code/sdk/dist/src/pending/pending-command-set.d.ts +361 -0
- package/node_modules/@muse-code/sdk/dist/src/pending/pending-command-set.js +468 -0
- package/node_modules/@muse-code/sdk/package.json +47 -0
- package/package.json +8 -4
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The schema-bundle staleness anchor (spec 14990 FR-006).
|
|
3
|
+
*
|
|
4
|
+
* `EXPECTED_SCHEMA_FINGERPRINT` pins the stable protocol-schema fingerprint
|
|
5
|
+
* this SDK was written against. Two consumers:
|
|
6
|
+
*
|
|
7
|
+
* - Build time: a test asserts this equals `schema/msp/stable/manifest.json`,
|
|
8
|
+
* so a schema advance that forgets the SDK reds the SDK lane instead of
|
|
9
|
+
* shipping a silently-stale facade. This is the same manifest-pin pattern
|
|
10
|
+
* the #210 transcripts use.
|
|
11
|
+
* - Runtime: the connection machine compares `InitializeResult.schema
|
|
12
|
+
* .fingerprint` against this pin. A mismatch is a WARNING, never an error
|
|
13
|
+
* (tdd SS1.4.1) — additive-optional evolution means an older SDK keeps
|
|
14
|
+
* working against a newer host.
|
|
15
|
+
*/
|
|
16
|
+
export declare const EXPECTED_SCHEMA_FINGERPRINT = "sha256:eae467ab60cb9a5d5af4de616830f065e59375c987fd59269c8cc33ed2a1137f";
|
|
17
|
+
/**
|
|
18
|
+
* What a build-time mismatch MEANS, in the words a human needs at 2am.
|
|
19
|
+
*
|
|
20
|
+
* A mismatch is the normal, expected consequence of the protocol schema
|
|
21
|
+
* advancing — it is NOT evidence that the SDK is broken. The remedy is to
|
|
22
|
+
* re-pin and adapt whatever actually changed. Several lanes have burned time
|
|
23
|
+
* reading a red fingerprint as an SDK defect, so the message says it plainly.
|
|
24
|
+
*/
|
|
25
|
+
export declare function fingerprintMismatchMessage(pinned: string, actual: string): string;
|
|
26
|
+
/** The runtime arm (tdd SS1.4.1): advisory only, never a connection failure. */
|
|
27
|
+
export interface FingerprintWarning {
|
|
28
|
+
readonly kind: "schemaFingerprintMismatch";
|
|
29
|
+
readonly pinned: string;
|
|
30
|
+
readonly served: string;
|
|
31
|
+
readonly message: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Compare a host's advertised fingerprint against the pin.
|
|
35
|
+
* Returns `undefined` when they agree — a mismatch is surfaced, not thrown.
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkServedFingerprint(served: string, pinned?: string): FingerprintWarning | undefined;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The schema-bundle staleness anchor (spec 14990 FR-006).
|
|
3
|
+
*
|
|
4
|
+
* `EXPECTED_SCHEMA_FINGERPRINT` pins the stable protocol-schema fingerprint
|
|
5
|
+
* this SDK was written against. Two consumers:
|
|
6
|
+
*
|
|
7
|
+
* - Build time: a test asserts this equals `schema/msp/stable/manifest.json`,
|
|
8
|
+
* so a schema advance that forgets the SDK reds the SDK lane instead of
|
|
9
|
+
* shipping a silently-stale facade. This is the same manifest-pin pattern
|
|
10
|
+
* the #210 transcripts use.
|
|
11
|
+
* - Runtime: the connection machine compares `InitializeResult.schema
|
|
12
|
+
* .fingerprint` against this pin. A mismatch is a WARNING, never an error
|
|
13
|
+
* (tdd SS1.4.1) — additive-optional evolution means an older SDK keeps
|
|
14
|
+
* working against a newer host.
|
|
15
|
+
*/
|
|
16
|
+
export const EXPECTED_SCHEMA_FINGERPRINT = "sha256:eae467ab60cb9a5d5af4de616830f065e59375c987fd59269c8cc33ed2a1137f";
|
|
17
|
+
/**
|
|
18
|
+
* What a build-time mismatch MEANS, in the words a human needs at 2am.
|
|
19
|
+
*
|
|
20
|
+
* A mismatch is the normal, expected consequence of the protocol schema
|
|
21
|
+
* advancing — it is NOT evidence that the SDK is broken. The remedy is to
|
|
22
|
+
* re-pin and adapt whatever actually changed. Several lanes have burned time
|
|
23
|
+
* reading a red fingerprint as an SDK defect, so the message says it plainly.
|
|
24
|
+
*/
|
|
25
|
+
export function fingerprintMismatchMessage(pinned, actual) {
|
|
26
|
+
return [
|
|
27
|
+
"MSP schema fingerprint moved: re-pin after a schema advance.",
|
|
28
|
+
` pinned by @muse-code/sdk: ${pinned}`,
|
|
29
|
+
` schema/msp/stable/manifest.json: ${actual}`,
|
|
30
|
+
"",
|
|
31
|
+
"This does NOT mean the SDK is broken. The stable schema bundle advanced",
|
|
32
|
+
"(a #206 enrollment or renderer change). Fix it by running",
|
|
33
|
+
"scripts/regen-msp-pins.sh, which re-pins EXPECTED_SCHEMA_FINGERPRINT and",
|
|
34
|
+
"every other derived copy from the committed stable manifest, then adapt",
|
|
35
|
+
"the facade to whatever the advance actually changed. Never hand-edit",
|
|
36
|
+
"schema/msp/** to match the SDK.",
|
|
37
|
+
].join("\n");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Compare a host's advertised fingerprint against the pin.
|
|
41
|
+
* Returns `undefined` when they agree — a mismatch is surfaced, not thrown.
|
|
42
|
+
*/
|
|
43
|
+
export function checkServedFingerprint(served, pinned = EXPECTED_SCHEMA_FINGERPRINT) {
|
|
44
|
+
if (served === pinned)
|
|
45
|
+
return undefined;
|
|
46
|
+
return {
|
|
47
|
+
kind: "schemaFingerprintMismatch",
|
|
48
|
+
pinned,
|
|
49
|
+
served,
|
|
50
|
+
message: `host schema fingerprint ${served} differs from the fingerprint this ` +
|
|
51
|
+
`SDK pins (${pinned}); proceeding under additive-optional evolution ` +
|
|
52
|
+
`(SS1.5.4). Re-pin after a schema advance.`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The item half of the SS4 client fold: upsert-by-revision plus delta
|
|
3
|
+
* accumulation (spec 14990 FR-007, INV-003/INV-004).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS IS GENERIC. The concrete wire item type (`Item`, `ItemDeltaParams`,
|
|
6
|
+
* …) is rendered by `crates/protocol` and is not yet in the committed
|
|
7
|
+
* declarations — the session-view plane is enrolled by #14953. INV-001
|
|
8
|
+
* forbids hand-writing a protocol type to fill the gap, so this module states
|
|
9
|
+
* only the *algebraic precondition* its rules need — an item has an identity
|
|
10
|
+
* and a revision — and takes the payload as a type parameter. When #14953
|
|
11
|
+
* lands, `SessionFold` binds `T` to `@muse-code/msp`'s `Item` with no change here
|
|
12
|
+
* and no protocol shape ever restated locally.
|
|
13
|
+
*/
|
|
14
|
+
/** The minimum an upsert needs: identity plus the monotonic ordering guard. */
|
|
15
|
+
export interface RevisionedItem {
|
|
16
|
+
readonly itemId: string;
|
|
17
|
+
readonly revision: number;
|
|
18
|
+
}
|
|
19
|
+
/** What an apply did — enough for a consumer to render without re-diffing. */
|
|
20
|
+
export type ItemApplyOutcome = {
|
|
21
|
+
readonly kind: "inserted";
|
|
22
|
+
readonly itemId: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly kind: "replaced";
|
|
25
|
+
readonly itemId: string;
|
|
26
|
+
readonly from: number;
|
|
27
|
+
readonly to: number;
|
|
28
|
+
}
|
|
29
|
+
/** A stale or replayed re-emission. The store did not change (INV-003). */
|
|
30
|
+
| {
|
|
31
|
+
readonly kind: "ignoredStaleRevision";
|
|
32
|
+
readonly itemId: string;
|
|
33
|
+
readonly held: number;
|
|
34
|
+
readonly offered: number;
|
|
35
|
+
};
|
|
36
|
+
/** What a delta did. Deltas bump no revision (tdd SS4.4.1). */
|
|
37
|
+
export type DeltaApplyOutcome = {
|
|
38
|
+
readonly kind: "appended";
|
|
39
|
+
readonly itemId: string;
|
|
40
|
+
readonly field: string;
|
|
41
|
+
readonly length: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A delta for an item the store does not hold. Deltas are a streaming
|
|
45
|
+
* optimization and are never a fold's only source of a fact (tdd SS4.7.3),
|
|
46
|
+
* so this is buffered against the item's arrival rather than dropped or
|
|
47
|
+
* treated as an error.
|
|
48
|
+
*/
|
|
49
|
+
| {
|
|
50
|
+
readonly kind: "bufferedForAbsentItem";
|
|
51
|
+
readonly itemId: string;
|
|
52
|
+
readonly field: string;
|
|
53
|
+
};
|
|
54
|
+
/** Client-only annotation required when an ephemeral host dies abnormally. */
|
|
55
|
+
export interface TerminalUnknownItemAnnotation {
|
|
56
|
+
readonly kind: "terminalUnknown";
|
|
57
|
+
readonly itemId: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Items in first-opened order (the order tdd SS4.9.1 requires of a snapshot),
|
|
61
|
+
* each held at its latest revision, with per-field delta accumulators.
|
|
62
|
+
*/
|
|
63
|
+
export declare class ItemStore<T extends RevisionedItem> {
|
|
64
|
+
#private;
|
|
65
|
+
/**
|
|
66
|
+
* Apply an `item/started` | `item/updated` | `item/completed` payload.
|
|
67
|
+
* Replace iff the incoming revision is strictly higher (INV-003).
|
|
68
|
+
*/
|
|
69
|
+
apply(item: T): ItemApplyOutcome;
|
|
70
|
+
/**
|
|
71
|
+
* Apply an `item/delta`: a UTF-8-safe append to the open item's field named
|
|
72
|
+
* by `field` (default `"text"`), contiguous and lossless in cursor order.
|
|
73
|
+
* The concatenation of all deltas for a field path equals that field's
|
|
74
|
+
* value on the final `item/completed` object (INV-004, tdd SS4.3.1).
|
|
75
|
+
*/
|
|
76
|
+
applyDelta(itemId: string, delta: string, field?: string): DeltaApplyOutcome;
|
|
77
|
+
/** The accumulated delta text for a field path, or `undefined` if none. */
|
|
78
|
+
accumulated(itemId: string, field?: string): string | undefined;
|
|
79
|
+
/** Every field path that has accumulated deltas for this item. */
|
|
80
|
+
accumulatedFields(itemId: string): readonly string[];
|
|
81
|
+
get(itemId: string): T | undefined;
|
|
82
|
+
has(itemId: string): boolean;
|
|
83
|
+
/** Items in first-opened order (tdd SS4.9.1). */
|
|
84
|
+
list(): readonly T[];
|
|
85
|
+
/** The id of the most recently opened item, or `undefined` when empty. */
|
|
86
|
+
lastOpenedItemId(): string | undefined;
|
|
87
|
+
get size(): number;
|
|
88
|
+
/**
|
|
89
|
+
* Seed from a snapshot's `state.items` (tdd SS4.9.1): every item at its
|
|
90
|
+
* latest revision at the snapshot cursor, in first-opened order. Seeding
|
|
91
|
+
* REPLACES the store — a snapshot is authoritative (tdd SS4.9), never
|
|
92
|
+
* merged into stale local state.
|
|
93
|
+
*
|
|
94
|
+
* Delta accumulators are intentionally NOT seeded: a snapshot carries
|
|
95
|
+
* streamed fields at their accumulated-so-far values on the item itself,
|
|
96
|
+
* and `view/page` never replays ephemeral-sourced deltas (tdd SS4.7.3).
|
|
97
|
+
*/
|
|
98
|
+
seed(items: Iterable<T>): void;
|
|
99
|
+
/**
|
|
100
|
+
* Permanently discard an ephemeral session after abnormal host death.
|
|
101
|
+
*
|
|
102
|
+
* The last wire item remains byte-for-byte intact: terminal-unknown is a
|
|
103
|
+
* client display annotation, never a fabricated `item/completed` or wire
|
|
104
|
+
* status (SS2.13.3b / SS4.4.3). Further events and snapshot seeding are
|
|
105
|
+
* refused because this session has no resume surface.
|
|
106
|
+
*/
|
|
107
|
+
markEphemeralHostDeath(isInProgress: (item: T) => boolean): readonly TerminalUnknownItemAnnotation[];
|
|
108
|
+
isTerminalUnknown(itemId: string): boolean;
|
|
109
|
+
get ephemeralSessionDiscarded(): boolean;
|
|
110
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The item half of the SS4 client fold: upsert-by-revision plus delta
|
|
3
|
+
* accumulation (spec 14990 FR-007, INV-003/INV-004).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS IS GENERIC. The concrete wire item type (`Item`, `ItemDeltaParams`,
|
|
6
|
+
* …) is rendered by `crates/protocol` and is not yet in the committed
|
|
7
|
+
* declarations — the session-view plane is enrolled by #14953. INV-001
|
|
8
|
+
* forbids hand-writing a protocol type to fill the gap, so this module states
|
|
9
|
+
* only the *algebraic precondition* its rules need — an item has an identity
|
|
10
|
+
* and a revision — and takes the payload as a type parameter. When #14953
|
|
11
|
+
* lands, `SessionFold` binds `T` to `@muse-code/msp`'s `Item` with no change here
|
|
12
|
+
* and no protocol shape ever restated locally.
|
|
13
|
+
*/
|
|
14
|
+
import { MuseSessionDiscardedError } from "../errors.js";
|
|
15
|
+
/**
|
|
16
|
+
* Items in first-opened order (the order tdd SS4.9.1 requires of a snapshot),
|
|
17
|
+
* each held at its latest revision, with per-field delta accumulators.
|
|
18
|
+
*/
|
|
19
|
+
export class ItemStore {
|
|
20
|
+
#items = new Map();
|
|
21
|
+
/** itemId -> field path -> accumulated text. */
|
|
22
|
+
#deltas = new Map();
|
|
23
|
+
/** First-opened order; an itemId appears exactly once. */
|
|
24
|
+
#order = [];
|
|
25
|
+
#terminalUnknown = new Set();
|
|
26
|
+
#ephemeralSessionDiscarded = false;
|
|
27
|
+
/**
|
|
28
|
+
* Apply an `item/started` | `item/updated` | `item/completed` payload.
|
|
29
|
+
* Replace iff the incoming revision is strictly higher (INV-003).
|
|
30
|
+
*/
|
|
31
|
+
apply(item) {
|
|
32
|
+
this.#assertSessionActive();
|
|
33
|
+
const held = this.#items.get(item.itemId);
|
|
34
|
+
if (held === undefined) {
|
|
35
|
+
this.#items.set(item.itemId, item);
|
|
36
|
+
this.#order.push(item.itemId);
|
|
37
|
+
return { kind: "inserted", itemId: item.itemId };
|
|
38
|
+
}
|
|
39
|
+
if (item.revision <= held.revision) {
|
|
40
|
+
return {
|
|
41
|
+
kind: "ignoredStaleRevision",
|
|
42
|
+
itemId: item.itemId,
|
|
43
|
+
held: held.revision,
|
|
44
|
+
offered: item.revision,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
this.#items.set(item.itemId, item);
|
|
48
|
+
return {
|
|
49
|
+
kind: "replaced",
|
|
50
|
+
itemId: item.itemId,
|
|
51
|
+
from: held.revision,
|
|
52
|
+
to: item.revision,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Apply an `item/delta`: a UTF-8-safe append to the open item's field named
|
|
57
|
+
* by `field` (default `"text"`), contiguous and lossless in cursor order.
|
|
58
|
+
* The concatenation of all deltas for a field path equals that field's
|
|
59
|
+
* value on the final `item/completed` object (INV-004, tdd SS4.3.1).
|
|
60
|
+
*/
|
|
61
|
+
applyDelta(itemId, delta, field = "text") {
|
|
62
|
+
this.#assertSessionActive();
|
|
63
|
+
let fields = this.#deltas.get(itemId);
|
|
64
|
+
if (fields === undefined) {
|
|
65
|
+
fields = new Map();
|
|
66
|
+
this.#deltas.set(itemId, fields);
|
|
67
|
+
}
|
|
68
|
+
fields.set(field, (fields.get(field) ?? "") + delta);
|
|
69
|
+
if (!this.#items.has(itemId)) {
|
|
70
|
+
return { kind: "bufferedForAbsentItem", itemId, field };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
kind: "appended",
|
|
74
|
+
itemId,
|
|
75
|
+
field,
|
|
76
|
+
length: (fields.get(field) ?? "").length,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** The accumulated delta text for a field path, or `undefined` if none. */
|
|
80
|
+
accumulated(itemId, field = "text") {
|
|
81
|
+
return this.#deltas.get(itemId)?.get(field);
|
|
82
|
+
}
|
|
83
|
+
/** Every field path that has accumulated deltas for this item. */
|
|
84
|
+
accumulatedFields(itemId) {
|
|
85
|
+
const fields = this.#deltas.get(itemId);
|
|
86
|
+
return fields === undefined ? [] : [...fields.keys()].sort();
|
|
87
|
+
}
|
|
88
|
+
get(itemId) {
|
|
89
|
+
return this.#items.get(itemId);
|
|
90
|
+
}
|
|
91
|
+
has(itemId) {
|
|
92
|
+
return this.#items.has(itemId);
|
|
93
|
+
}
|
|
94
|
+
/** Items in first-opened order (tdd SS4.9.1). */
|
|
95
|
+
list() {
|
|
96
|
+
const out = [];
|
|
97
|
+
for (const id of this.#order) {
|
|
98
|
+
const item = this.#items.get(id);
|
|
99
|
+
if (item !== undefined)
|
|
100
|
+
out.push(item);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/** The id of the most recently opened item, or `undefined` when empty. */
|
|
105
|
+
lastOpenedItemId() {
|
|
106
|
+
return this.#order.at(-1);
|
|
107
|
+
}
|
|
108
|
+
get size() {
|
|
109
|
+
return this.#items.size;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Seed from a snapshot's `state.items` (tdd SS4.9.1): every item at its
|
|
113
|
+
* latest revision at the snapshot cursor, in first-opened order. Seeding
|
|
114
|
+
* REPLACES the store — a snapshot is authoritative (tdd SS4.9), never
|
|
115
|
+
* merged into stale local state.
|
|
116
|
+
*
|
|
117
|
+
* Delta accumulators are intentionally NOT seeded: a snapshot carries
|
|
118
|
+
* streamed fields at their accumulated-so-far values on the item itself,
|
|
119
|
+
* and `view/page` never replays ephemeral-sourced deltas (tdd SS4.7.3).
|
|
120
|
+
*/
|
|
121
|
+
seed(items) {
|
|
122
|
+
this.#assertSessionActive();
|
|
123
|
+
this.#items.clear();
|
|
124
|
+
this.#deltas.clear();
|
|
125
|
+
this.#terminalUnknown.clear();
|
|
126
|
+
this.#order.length = 0;
|
|
127
|
+
for (const item of items) {
|
|
128
|
+
if (!this.#items.has(item.itemId))
|
|
129
|
+
this.#order.push(item.itemId);
|
|
130
|
+
this.#items.set(item.itemId, item);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Permanently discard an ephemeral session after abnormal host death.
|
|
135
|
+
*
|
|
136
|
+
* The last wire item remains byte-for-byte intact: terminal-unknown is a
|
|
137
|
+
* client display annotation, never a fabricated `item/completed` or wire
|
|
138
|
+
* status (SS2.13.3b / SS4.4.3). Further events and snapshot seeding are
|
|
139
|
+
* refused because this session has no resume surface.
|
|
140
|
+
*/
|
|
141
|
+
markEphemeralHostDeath(
|
|
142
|
+
// The store never knows wire shapes (INV-001), so the caller supplies the
|
|
143
|
+
// in-progress probe — the slice-3 `SessionFold` binding passes
|
|
144
|
+
// `(item) => item.status === "inProgress"` against the generated `Item`
|
|
145
|
+
// type, where the compiler checks the field (review round 2026-08-15).
|
|
146
|
+
isInProgress) {
|
|
147
|
+
if (!this.#ephemeralSessionDiscarded) {
|
|
148
|
+
for (const item of this.#items.values()) {
|
|
149
|
+
if (isInProgress(item))
|
|
150
|
+
this.#terminalUnknown.add(item.itemId);
|
|
151
|
+
}
|
|
152
|
+
this.#ephemeralSessionDiscarded = true;
|
|
153
|
+
}
|
|
154
|
+
return this.#order
|
|
155
|
+
.filter((itemId) => this.#terminalUnknown.has(itemId))
|
|
156
|
+
.map((itemId) => ({ kind: "terminalUnknown", itemId }));
|
|
157
|
+
}
|
|
158
|
+
isTerminalUnknown(itemId) {
|
|
159
|
+
return this.#terminalUnknown.has(itemId);
|
|
160
|
+
}
|
|
161
|
+
get ephemeralSessionDiscarded() {
|
|
162
|
+
return this.#ephemeralSessionDiscarded;
|
|
163
|
+
}
|
|
164
|
+
#assertSessionActive() {
|
|
165
|
+
if (this.#ephemeralSessionDiscarded) {
|
|
166
|
+
throw new MuseSessionDiscardedError("ephemeral session was discarded after abnormal host death");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|