@sagentlab/navarch-runtime 0.1.116 → 0.1.118
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 +43 -17
- package/bin/muse-mcp-bridge.cjs +3 -0
- package/bin/worktree-guard-hook.cjs +74 -2
- package/dist/adapters/index.cjs +6 -1
- package/dist/adapters/muse.cjs +433 -0
- package/dist/agent-auth.cjs +2 -1
- package/dist/config.cjs +8 -1
- package/dist/conversation/capability.cjs +1 -1
- package/dist/exit-conditions.cjs +1 -1
- package/dist/muse-mcp-bridge.cjs +167 -0
- package/dist/session.cjs +30 -8
- 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,536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `SessionFold` — the SS4 client fold, bound to the generated wire types
|
|
3
|
+
* (spec 14990 FR-007, tasks.md T019).
|
|
4
|
+
*
|
|
5
|
+
* `ItemStore` and `SessionStateStore` are generic because they were written
|
|
6
|
+
* before the session-view plane entered the generated declarations. This
|
|
7
|
+
* module is where the generic parameters are BOUND: items are `@muse-code/msp`'s
|
|
8
|
+
* `Item`, state-family values are the generated `session/*` params objects,
|
|
9
|
+
* and every event this fold accepts is typed as the params object the wire
|
|
10
|
+
* actually carries. Nothing here restates a wire shape (INV-001).
|
|
11
|
+
*
|
|
12
|
+
* Beyond the two-store composition, the fold owns the parts of the view that
|
|
13
|
+
* are neither an item nor a state family:
|
|
14
|
+
*
|
|
15
|
+
* - the TURN LIFECYCLE — `turn/started`, `turn/completed`, `turn/retracted`,
|
|
16
|
+
* `turn/unqueued`, and the non-terminal `turn/retryScheduled` and
|
|
17
|
+
* `turn/foregroundCompleted`;
|
|
18
|
+
* - the APPROVAL and USER-INPUT view events as fold INPUTS — the pending
|
|
19
|
+
* sets and their first durable terminal. The decision flow (choosing and
|
|
20
|
+
* sending a resolution) is the facade's, not the fold's;
|
|
21
|
+
* - the DELIVERY marker `view/gap` — it seeds no store, it moves the fold's
|
|
22
|
+
* CURRENCY (`pendingGap`/`current`, FM-003). The recovery that fills the
|
|
23
|
+
* hole needs client→server I/O and is therefore the facade's (T032).
|
|
24
|
+
*
|
|
25
|
+
* INV-006 governs all of it: the fold never invents a terminal. A retract and
|
|
26
|
+
* a reclaim are not `TurnTerminal` values and are never rendered as one, an
|
|
27
|
+
* unrecognized `TurnTerminal` is kept verbatim, and a second resolution never
|
|
28
|
+
* displaces the first durable one.
|
|
29
|
+
*/
|
|
30
|
+
import type { ApprovalRequestParams, ApprovalResolvedParams, ApprovalUpdatedParams, Item, ItemCompletedParams, ItemDeltaParams, ItemStartedParams, ItemUpdatedParams, MspNotification, SessionApprovalModeChangedParams, SessionBranchChangedParams, SessionContextUsageParams, SessionGoalChangedParams, SessionModelChangedParams, SessionModelRouteUnservedParams, SessionNameChangedParams, SessionReasoningEffortChangedParams, SessionTodoListChangedParams, SessionTokenUsageParams, TurnCompletedParams, TurnError, TurnForegroundCompletedParams, TurnRetractedParams, TurnRetryScheduledParams, TurnStartedParams, TurnTerminal, TurnUnqueuedParams, UserInputRequestParams, UserInputSettledParams, ViewGapParams } from "../msp.js";
|
|
31
|
+
import type { DeltaApplyOutcome, ItemApplyOutcome, TerminalUnknownItemAnnotation } from "./item-store.js";
|
|
32
|
+
import type { StateApplyOutcome, StateValue } from "./state-store.js";
|
|
33
|
+
/** The session-state families, each keyed in the store by its method name. */
|
|
34
|
+
export type SessionStateParams = SessionApprovalModeChangedParams | SessionBranchChangedParams | SessionContextUsageParams | SessionGoalChangedParams | SessionModelChangedParams | SessionNameChangedParams | SessionReasoningEffortChangedParams | SessionTodoListChangedParams | SessionTokenUsageParams;
|
|
35
|
+
/** A view notification, paired with the generated params object it carries. */
|
|
36
|
+
export type ViewEvent = {
|
|
37
|
+
readonly method: "item/started";
|
|
38
|
+
readonly params: ItemStartedParams;
|
|
39
|
+
} | {
|
|
40
|
+
readonly method: "item/updated";
|
|
41
|
+
readonly params: ItemUpdatedParams;
|
|
42
|
+
} | {
|
|
43
|
+
readonly method: "item/completed";
|
|
44
|
+
readonly params: ItemCompletedParams;
|
|
45
|
+
} | {
|
|
46
|
+
readonly method: "item/delta";
|
|
47
|
+
readonly params: ItemDeltaParams;
|
|
48
|
+
} | {
|
|
49
|
+
readonly method: "turn/started";
|
|
50
|
+
readonly params: TurnStartedParams;
|
|
51
|
+
} | {
|
|
52
|
+
readonly method: "turn/completed";
|
|
53
|
+
readonly params: TurnCompletedParams;
|
|
54
|
+
} | {
|
|
55
|
+
readonly method: "turn/foregroundCompleted";
|
|
56
|
+
readonly params: TurnForegroundCompletedParams;
|
|
57
|
+
} | {
|
|
58
|
+
readonly method: "turn/retracted";
|
|
59
|
+
readonly params: TurnRetractedParams;
|
|
60
|
+
} | {
|
|
61
|
+
readonly method: "turn/retryScheduled";
|
|
62
|
+
readonly params: TurnRetryScheduledParams;
|
|
63
|
+
} | {
|
|
64
|
+
readonly method: "turn/unqueued";
|
|
65
|
+
readonly params: TurnUnqueuedParams;
|
|
66
|
+
} | {
|
|
67
|
+
readonly method: "approval/requested";
|
|
68
|
+
readonly params: ApprovalRequestParams;
|
|
69
|
+
} | {
|
|
70
|
+
readonly method: "approval/updated";
|
|
71
|
+
readonly params: ApprovalUpdatedParams;
|
|
72
|
+
} | {
|
|
73
|
+
readonly method: "approval/resolved";
|
|
74
|
+
readonly params: ApprovalResolvedParams;
|
|
75
|
+
} | {
|
|
76
|
+
readonly method: "userInput/requested";
|
|
77
|
+
readonly params: UserInputRequestParams;
|
|
78
|
+
} | {
|
|
79
|
+
readonly method: "userInput/settled";
|
|
80
|
+
readonly params: UserInputSettledParams;
|
|
81
|
+
} | {
|
|
82
|
+
readonly method: "session/modelChanged";
|
|
83
|
+
readonly params: SessionModelChangedParams;
|
|
84
|
+
} | {
|
|
85
|
+
readonly method: "session/reasoningEffortChanged";
|
|
86
|
+
readonly params: SessionReasoningEffortChangedParams;
|
|
87
|
+
} | {
|
|
88
|
+
readonly method: "session/goalChanged";
|
|
89
|
+
readonly params: SessionGoalChangedParams;
|
|
90
|
+
} | {
|
|
91
|
+
readonly method: "session/todoListChanged";
|
|
92
|
+
readonly params: SessionTodoListChangedParams;
|
|
93
|
+
} | {
|
|
94
|
+
readonly method: "session/branchChanged";
|
|
95
|
+
readonly params: SessionBranchChangedParams;
|
|
96
|
+
} | {
|
|
97
|
+
readonly method: "session/tokenUsage";
|
|
98
|
+
readonly params: SessionTokenUsageParams;
|
|
99
|
+
} | {
|
|
100
|
+
readonly method: "session/contextUsage";
|
|
101
|
+
readonly params: SessionContextUsageParams;
|
|
102
|
+
} | {
|
|
103
|
+
readonly method: "session/approvalModeChanged";
|
|
104
|
+
readonly params: SessionApprovalModeChangedParams;
|
|
105
|
+
} | {
|
|
106
|
+
readonly method: "session/nameChanged";
|
|
107
|
+
readonly params: SessionNameChangedParams;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The one-shot login-swap route disclosure (#25603, tdd SS4.6.8): routed to
|
|
111
|
+
* its own non-storing outcome — a notice, never sticky session state.
|
|
112
|
+
*/
|
|
113
|
+
| {
|
|
114
|
+
readonly method: "session/modelRouteUnserved";
|
|
115
|
+
readonly params: SessionModelRouteUnservedParams;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The delivery-plane marker (tdd SS4.8). A `ViewEvent` since spec 14990
|
|
119
|
+
* T032, and the ONE arm that changes no session state: it names a hole in
|
|
120
|
+
* DELIVERY rather than a change in the view, so what it moves is the fold's
|
|
121
|
+
* CURRENCY — see `pendingGap` below.
|
|
122
|
+
*
|
|
123
|
+
* D-24021-1 excluded it deliberately and left the flip to this lane
|
|
124
|
+
* ("that lane owns removing the exclusion"). The flip is recorded in
|
|
125
|
+
* `specs/14990-muse-sdk/decisions/23980-fold-the-view-gap-marker.md`. What
|
|
126
|
+
* changed is not the structural fact D-24021-1 named — the marker still
|
|
127
|
+
* carries no `viewCursor` and no `sourceRange`, and still seeds nothing into
|
|
128
|
+
* either store — but the SDK's answer to it: a frame this package fully
|
|
129
|
+
* recognizes may not be reported as `ignoredUnrecognizedMethod`, because
|
|
130
|
+
* that is the report reserved for a NEWER host's method this SDK has never
|
|
131
|
+
* heard of (SS1.5.4), and a consumer told that cannot tell a tolerated
|
|
132
|
+
* unknown from an unfilled hole.
|
|
133
|
+
*/
|
|
134
|
+
| {
|
|
135
|
+
readonly method: "view/gap";
|
|
136
|
+
readonly params: ViewGapParams;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Compile-time coverage: every `MspNotification` except the handshake's
|
|
140
|
+
* `initialized` and the live host-state projections is a fold input. A #206
|
|
141
|
+
* enrollment that adds a view notification fails HERE rather than being
|
|
142
|
+
* silently ignored at runtime.
|
|
143
|
+
*
|
|
144
|
+
* The exclusions, each excluded because it is NOT a view event:
|
|
145
|
+
* - `initialized` is a HANDSHAKE notification, answered by `MspHandshake`
|
|
146
|
+
* before this fold exists. (The `view/gap` exclusion D-24021-1 recorded
|
|
147
|
+
* was removed by spec 14990 T032 when the marker became the `ViewEvent`
|
|
148
|
+
* arm above.)
|
|
149
|
+
* - `session/started` and `session/closed` (#33065's enrollment) are SS2.6
|
|
150
|
+
* control-plane lifecycle broadcasts, not view events — they carry no
|
|
151
|
+
* foldable view envelope (no `sourceRange`; `closed`'s `viewCursor` is a
|
|
152
|
+
* final session head, not view state), and a fold over session-view state
|
|
153
|
+
* has nothing to apply. Today a lifecycle frame keeps the pre-enrollment
|
|
154
|
+
* runtime answer (`ignoredUnrecognizedMethod`, pinned by the session-fold
|
|
155
|
+
* test); #33102 owns consciously surfacing the pair, the same sanctioned
|
|
156
|
+
* initial-drop shape D-24021-1 used for `view/gap` before T032 flipped it.
|
|
157
|
+
* - `session/viewHealthChanged` (#32557, ADR 32557) is a COMMAND-PLANE
|
|
158
|
+
* health push, not a view-fold event: it reports that the live view
|
|
159
|
+
* stream stopped, so it carries no cursor and folds into no transcript
|
|
160
|
+
* state. A consumer handles it out of band (mark the session view
|
|
161
|
+
* unhealthy), never through this fold.
|
|
162
|
+
* - `usage/changed` (ADR 32563 D3, tdd SS3.23) is a live HOST-STATE push
|
|
163
|
+
* outside the view altitude: no `viewCursor`, no `sourceRange`, nothing
|
|
164
|
+
* seeded into either store; it reaches consumers through the ordinary
|
|
165
|
+
* notification hook, not this fold.
|
|
166
|
+
* - `skill/changed` (ADR 32471 D3, tdd SS3.22.2) is a live host-state
|
|
167
|
+
* projection carrying `sessionId` only — no `viewCursor`, no
|
|
168
|
+
* `sourceRange`, nothing durable to fold. The SDK consumer's reaction is
|
|
169
|
+
* an ACTION (re-issue `skill/list`), not fold state, so it rides the
|
|
170
|
+
* client's notification callback rather than this fold.
|
|
171
|
+
*/
|
|
172
|
+
type AssertNever<T extends never> = T;
|
|
173
|
+
type LiveHostStateProjection = "skill/changed" | "usage/changed" | "session/statusChanged" | "session/listChanged" | "plugin/changed" | "plugin/marketplaceChanged" | "computerUseSetup/changed";
|
|
174
|
+
type SessionLifecycleBroadcast = "session/started" | "session/closed";
|
|
175
|
+
type NonFoldNotification = "initialized" | "userInput/engaged" | "session/viewHealthChanged" | "recap/completed" | SessionLifecycleBroadcast | LiveHostStateProjection;
|
|
176
|
+
type UnfoldedViewNotification = Exclude<MspNotification, NonFoldNotification | ViewEvent["method"]>;
|
|
177
|
+
export type EveryViewNotificationIsFolded = AssertNever<UnfoldedViewNotification>;
|
|
178
|
+
/**
|
|
179
|
+
* The reverse direction: every `ViewEvent` arm names a REAL generated
|
|
180
|
+
* notification. `Exclude` silently ignores excluder members outside the
|
|
181
|
+
* union, so without this a typo'd or later-retired arm would compile clean
|
|
182
|
+
* as dead code (PR #23087 review round).
|
|
183
|
+
*/
|
|
184
|
+
type StaleViewArm = Exclude<ViewEvent["method"], MspNotification>;
|
|
185
|
+
export type NoStaleViewArm = AssertNever<StaleViewArm>;
|
|
186
|
+
/**
|
|
187
|
+
* The same reverse pin for the hand-typed exclusion literals above:
|
|
188
|
+
* `Exclude` silently ignores excluder members outside the union, so a
|
|
189
|
+
* retired or renamed `initialized` — or a lifecycle name that leaves the
|
|
190
|
+
* union — would otherwise compile clean as dead vocabulary (the PR #23087
|
|
191
|
+
* rule, applied to this file's only other hand-typed method set).
|
|
192
|
+
*/
|
|
193
|
+
type StaleExclusion = Exclude<NonFoldNotification, MspNotification>;
|
|
194
|
+
export type NoStaleExclusion = AssertNever<StaleExclusion>;
|
|
195
|
+
/**
|
|
196
|
+
* A recursively read-only view of a value.
|
|
197
|
+
*
|
|
198
|
+
* The fold's snapshot getters hand back its OWN stored objects — copying
|
|
199
|
+
* every read would cost more than the fold saves — and the generated wire
|
|
200
|
+
* types carry no `readonly`, so without this a consumer could write through
|
|
201
|
+
* a snapshot into fold state. This is composition over the generated types,
|
|
202
|
+
* never a restatement of one (INV-001).
|
|
203
|
+
*
|
|
204
|
+
* `readonly` is compile-time only; this is a type-level seal against
|
|
205
|
+
* accidental writes, not a runtime freeze. The alternative — deep-freezing
|
|
206
|
+
* or cloning on every read — would be a real cost on the hot path for a
|
|
207
|
+
* hazard the compiler can price at zero (PR #23087 review round).
|
|
208
|
+
*/
|
|
209
|
+
export type DeepReadonly<T> = T extends (infer U)[] ? readonly DeepReadonly<U>[] : T extends ReadonlyArray<infer U> ? readonly DeepReadonly<U>[] : T extends object ? {
|
|
210
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
211
|
+
} : T;
|
|
212
|
+
/** The session-state family keys: the `session/*` half of `ViewEvent`. */
|
|
213
|
+
export type SessionStateMethod = ViewEvent["method"] & `session/${string}`;
|
|
214
|
+
/**
|
|
215
|
+
* The methods whose arms can drop a frame as stale. Narrow rather than the
|
|
216
|
+
* whole `ViewEvent["method"]`: a consumer switching on a stale drop would
|
|
217
|
+
* otherwise have to "handle" cases like `item/delta` that no arm produces,
|
|
218
|
+
* and could never match exhaustively. A future arm that starts dropping
|
|
219
|
+
* widens this deliberately (PR #23087 review round).
|
|
220
|
+
*/
|
|
221
|
+
export type StaleDroppableMethod = "turn/started" | "turn/completed" | "turn/foregroundCompleted" | "turn/retryScheduled" | "approval/requested" | "approval/updated" | "userInput/requested";
|
|
222
|
+
/**
|
|
223
|
+
* The `NoStaleViewArm` sibling for the union above: these are hand-typed
|
|
224
|
+
* literals, so without this pin a typo'd or later-retired member compiles
|
|
225
|
+
* clean and a consumer told to match `ignoredStaleFrame` exhaustively has to
|
|
226
|
+
* "handle" a case no arm can produce (PR #23087 review round).
|
|
227
|
+
*/
|
|
228
|
+
type StrayStaleDroppable = Exclude<StaleDroppableMethod, ViewEvent["method"]>;
|
|
229
|
+
export type EveryStaleDroppableIsWireVocabulary = AssertNever<StrayStaleDroppable>;
|
|
230
|
+
/** Where a turn stands. Client fold state — not a wire vocabulary. */
|
|
231
|
+
export type TurnState = "running" | "settled" | "retracted" | "unqueued";
|
|
232
|
+
/** A turn as the fold has observed it. */
|
|
233
|
+
export interface TurnEntry {
|
|
234
|
+
readonly turnId: string;
|
|
235
|
+
/** The submitting command, when an event carried it. */
|
|
236
|
+
readonly commandId?: string;
|
|
237
|
+
readonly state: TurnState;
|
|
238
|
+
/** The server's terminal, verbatim. Absent unless `turn/completed` folded. */
|
|
239
|
+
readonly terminal?: TurnTerminal;
|
|
240
|
+
/** Present iff the terminal was `"failed"` (tdd SS4.5.1). */
|
|
241
|
+
readonly error?: TurnError;
|
|
242
|
+
/** The latest scheduled model retry; non-terminal (tdd SS4.5.1, D-026). */
|
|
243
|
+
readonly retryScheduled?: TurnRetryScheduledParams;
|
|
244
|
+
/**
|
|
245
|
+
* The latest foreground-completed notice; non-terminal (tdd SS4.5.12).
|
|
246
|
+
* Tells a renderer the answer is done while `blockingAgents` hold the
|
|
247
|
+
* turn; clears on the turn's terminal.
|
|
248
|
+
*/
|
|
249
|
+
readonly foregroundCompleted?: TurnForegroundCompletedParams;
|
|
250
|
+
}
|
|
251
|
+
/** An approval awaiting its first durable terminal. */
|
|
252
|
+
export interface PendingApproval {
|
|
253
|
+
readonly approvalId: string;
|
|
254
|
+
readonly requested: ApprovalRequestParams;
|
|
255
|
+
/** The most recent `approval/updated` refresh, if any. */
|
|
256
|
+
readonly latestUpdate?: ApprovalUpdatedParams;
|
|
257
|
+
}
|
|
258
|
+
/** What one `apply` did — enough to drive a render without re-diffing. */
|
|
259
|
+
export type FoldOutcome = {
|
|
260
|
+
readonly kind: "item";
|
|
261
|
+
readonly outcome: ItemApplyOutcome;
|
|
262
|
+
} | {
|
|
263
|
+
readonly kind: "itemDelta";
|
|
264
|
+
readonly outcome: DeltaApplyOutcome;
|
|
265
|
+
} | {
|
|
266
|
+
readonly kind: "turn";
|
|
267
|
+
readonly turnId: string;
|
|
268
|
+
readonly state: TurnState;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* `outcome` is sealed for the same reason every getter is (D-14): the
|
|
272
|
+
* store hands back the very object it just stored, so an unsealed
|
|
273
|
+
* `current` is a live handle into fold state — `out.outcome.current
|
|
274
|
+
* .modelId = "..."` would mutate the fold with no cast and break INV-002
|
|
275
|
+
* replay equality. This and `approvalPending.requested` (#36949) are the
|
|
276
|
+
* two `FoldOutcome` arms that carry a params object; every other arm
|
|
277
|
+
* carries only ids, numbers, and booleans, which is why D-14 originally
|
|
278
|
+
* missed this one (#23556 item 2). Both seals are pinned by
|
|
279
|
+
* `@ts-expect-error` write probes in `session-fold.test.ts`.
|
|
280
|
+
*/
|
|
281
|
+
| {
|
|
282
|
+
readonly kind: "sessionState";
|
|
283
|
+
readonly outcome: DeepReadonly<StateApplyOutcome<SessionStateParams>>;
|
|
284
|
+
} | {
|
|
285
|
+
readonly kind: "approvalPending";
|
|
286
|
+
readonly approvalId: string;
|
|
287
|
+
/**
|
|
288
|
+
* The pending entry's retained request (#36949): the facade's
|
|
289
|
+
* `approval/updated` re-route needs the request-shape members, and
|
|
290
|
+
* carrying them on the verdict itself makes "the fold vouched for this
|
|
291
|
+
* entry" and "here is that entry" one fact instead of a re-lookup a
|
|
292
|
+
* contract drift could silently miss.
|
|
293
|
+
*/
|
|
294
|
+
readonly requested: DeepReadonly<ApprovalRequestParams>;
|
|
295
|
+
} | {
|
|
296
|
+
readonly kind: "approvalResolved";
|
|
297
|
+
readonly approvalId: string;
|
|
298
|
+
/** False when a durable terminal had already landed; the first wins. */
|
|
299
|
+
readonly firstTerminal: boolean;
|
|
300
|
+
} | {
|
|
301
|
+
readonly kind: "userInputPending";
|
|
302
|
+
readonly userInputId: string;
|
|
303
|
+
} | {
|
|
304
|
+
readonly kind: "userInputSettled";
|
|
305
|
+
readonly userInputId: string;
|
|
306
|
+
readonly firstSettlement: boolean;
|
|
307
|
+
} | {
|
|
308
|
+
/**
|
|
309
|
+
* A `view/gap` folded: push delivery dropped everything in the open
|
|
310
|
+
* interval `(after, next)`, so this fold is no longer current until a
|
|
311
|
+
* sanctioned recovery fills it (tdd SS4.8, FM-003).
|
|
312
|
+
*
|
|
313
|
+
* The bounds are the FRAME's own, verbatim — the coalesced outstanding
|
|
314
|
+
* hole is `pendingGap`, which is what a recovery walks. Reported rather
|
|
315
|
+
* than dropped because the SDK recognizes this frame completely: an
|
|
316
|
+
* `ignoredUnrecognizedMethod` here would be indistinguishable from a
|
|
317
|
+
* newer host's genuinely unknown method (SS1.5.4).
|
|
318
|
+
*/
|
|
319
|
+
readonly kind: "deliveryGap";
|
|
320
|
+
readonly after: string;
|
|
321
|
+
readonly next: string;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* The one-shot login-swap route disclosure (#25603, tdd SS4.6.8): a NOTICE,
|
|
325
|
+
* not a state family — it seeds no store (a repair via `session/setModel`
|
|
326
|
+
* would never clear a stored copy, and a snapshot reseed would wipe it), so
|
|
327
|
+
* the params ride the outcome verbatim for the consumer to render once.
|
|
328
|
+
*/
|
|
329
|
+
| {
|
|
330
|
+
readonly kind: "modelRouteUnserved";
|
|
331
|
+
readonly params: SessionModelRouteUnservedParams;
|
|
332
|
+
} | {
|
|
333
|
+
readonly kind: "ignoredUnrecognizedMethod";
|
|
334
|
+
readonly method: string;
|
|
335
|
+
} | {
|
|
336
|
+
/**
|
|
337
|
+
* A frame that carried no `params`. The generated `Notification`
|
|
338
|
+
* declares `params` optional ("omitted entirely when empty"), so the
|
|
339
|
+
* wire can legally deliver one — but no `ViewEvent` arm is foldable
|
|
340
|
+
* without its params object, every one of them reads at least a
|
|
341
|
+
* `sessionId`. Dropped rather than thrown: SS1.5.4's posture is that a
|
|
342
|
+
* frame this SDK cannot fold leaves the rest of the fold intact, and
|
|
343
|
+
* throwing here would kill the consumer's notification pump
|
|
344
|
+
* (PR #23087 review round).
|
|
345
|
+
*/
|
|
346
|
+
readonly kind: "ignoredMissingParams";
|
|
347
|
+
readonly method: string;
|
|
348
|
+
} | {
|
|
349
|
+
/**
|
|
350
|
+
* A recognized frame the fold deliberately dropped, leaving state
|
|
351
|
+
* unchanged: a `turn/started` or `turn/completed` for a turn that
|
|
352
|
+
* already left `running`, an `approval/updated` for an approval it
|
|
353
|
+
* never saw requested (or whose entry a resolution already deleted),
|
|
354
|
+
* or a redelivered `approval/requested` / `userInput/requested` for an
|
|
355
|
+
* approval/prompt that already has its durable terminal. Truthful by
|
|
356
|
+
* contract: a "pending" kind here would make a renderer show an entry
|
|
357
|
+
* the fold does not hold (PR #23087 review round).
|
|
358
|
+
*
|
|
359
|
+
* SCOPE. "Dropped" means only that THIS fold's state is unchanged; it
|
|
360
|
+
* does not mean the frame carried nothing. The one known post-terminal
|
|
361
|
+
* frame that carries a first-delivery fact — an `approval/updated`
|
|
362
|
+
* whose `policyPersistence` reports a FAILED `localPersistent` store
|
|
363
|
+
* write, which the server sends AFTER `approval/resolved` (tdd
|
|
364
|
+
* SS5.5/SS6.8) — is dropped here with no accessor holding the reason.
|
|
365
|
+
* Surfacing it is deferred to the facade slice: issue #23379.
|
|
366
|
+
*/
|
|
367
|
+
readonly kind: "ignoredStaleFrame";
|
|
368
|
+
readonly method: StaleDroppableMethod;
|
|
369
|
+
readonly id: string;
|
|
370
|
+
};
|
|
371
|
+
/**
|
|
372
|
+
* The fold's read-only view of its `ItemStore`. Every mutation goes through
|
|
373
|
+
* `apply()`; a directly reachable `seed`/`applyDelta` would let a consumer
|
|
374
|
+
* desync the composite view (items cleared, turn/pending maps not), breaking
|
|
375
|
+
* INV-002 replay equality (PR #23087 review round).
|
|
376
|
+
*
|
|
377
|
+
* An ALLOWLIST, stated explicitly: TypeScript never checks `Omit`'s keys
|
|
378
|
+
* against the type, so a renamed or newly added `ItemStore` mutator would
|
|
379
|
+
* land on this exported surface silently. An explicit member list admits
|
|
380
|
+
* only what is written here, in both drift directions.
|
|
381
|
+
*
|
|
382
|
+
* Hiding the mutator METHODS is only half the job. The values these
|
|
383
|
+
* accessors return ARE the fold's stored objects and the generated wire
|
|
384
|
+
* types carry no `readonly`, so `fold.items.get(id).revision = 0` used to
|
|
385
|
+
* typecheck AND stick — defeating the INV-003 revision guard and breaking
|
|
386
|
+
* INV-002 replay equality through the read surface the mutator allowlist
|
|
387
|
+
* was supposed to seal. Every accessor therefore returns `DeepReadonly`
|
|
388
|
+
* (PR #23087 review round).
|
|
389
|
+
*/
|
|
390
|
+
export interface FoldItems {
|
|
391
|
+
accumulated(itemId: string, field?: string): string | undefined;
|
|
392
|
+
accumulatedFields(itemId: string): readonly string[];
|
|
393
|
+
readonly ephemeralSessionDiscarded: boolean;
|
|
394
|
+
get(itemId: string): DeepReadonly<Item> | undefined;
|
|
395
|
+
has(itemId: string): boolean;
|
|
396
|
+
isTerminalUnknown(itemId: string): boolean;
|
|
397
|
+
lastOpenedItemId(): string | undefined;
|
|
398
|
+
list(): readonly DeepReadonly<Item>[];
|
|
399
|
+
readonly size: number;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* The fold's read-only view of its `SessionStateStore`, stated explicitly
|
|
403
|
+
* rather than derived: the store's `get`/`has` take a bare `string`, so
|
|
404
|
+
* `get("session/modelchanged")` would typecheck and silently read
|
|
405
|
+
* `undefined`. The family key IS protocol vocabulary, so name it — this
|
|
406
|
+
* surface goes public in this PR, and narrowing it later is a break.
|
|
407
|
+
*/
|
|
408
|
+
export interface FoldSessionState {
|
|
409
|
+
/**
|
|
410
|
+
* GENERIC in the family, so the return type is that family's params
|
|
411
|
+
* object rather than the 7-way union. With the union, a wrong-family cast
|
|
412
|
+
* (`get("session/goalChanged") as SessionTokenUsageParams`) was a legal
|
|
413
|
+
* narrowing and read the wrong shape silently — the same invisible-miss
|
|
414
|
+
* class the key narrowing closed on the argument side. The method→params
|
|
415
|
+
* map already exists in `ViewEvent`; this just reuses it.
|
|
416
|
+
*/
|
|
417
|
+
get<M extends SessionStateMethod>(family: M): DeepReadonly<StateValue<Extract<ViewEvent, {
|
|
418
|
+
method: M;
|
|
419
|
+
}>["params"]>> | undefined;
|
|
420
|
+
has(family: SessionStateMethod): boolean;
|
|
421
|
+
/**
|
|
422
|
+
* Every family that holds a value, in insertion order. Narrowed in the
|
|
423
|
+
* RETURN position too, so the obvious consumer loop —
|
|
424
|
+
* `for (const f of families()) get(f)` — round-trips against this same
|
|
425
|
+
* interface instead of forcing the bare-string cast the parameter
|
|
426
|
+
* narrowing exists to forbid.
|
|
427
|
+
*/
|
|
428
|
+
families(): readonly SessionStateMethod[];
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* The whole client-side session view: items, session state, turns, and the
|
|
432
|
+
* pending approval/user-input sets.
|
|
433
|
+
*/
|
|
434
|
+
export declare class SessionFold {
|
|
435
|
+
#private;
|
|
436
|
+
/** Items, snapshot-only: the mutators are `apply()`'s, not the consumer's. */
|
|
437
|
+
get items(): FoldItems;
|
|
438
|
+
/**
|
|
439
|
+
* Discharge the SS2.13.3b ephemeral host-death obligation over the item
|
|
440
|
+
* half: every item still in progress becomes terminal-unknown and the fold
|
|
441
|
+
* refuses further events (spec FM-002; slice 3's T030b).
|
|
442
|
+
*
|
|
443
|
+
* A FOLD-level method rather than a `FoldItems` entry on purpose. `items` is
|
|
444
|
+
* snapshot-only — an allowlist whose whole job is that a store mutator can
|
|
445
|
+
* never leak onto the consumer surface (PR #23087 review round) — and this
|
|
446
|
+
* IS a mutator. Widening the allowlist to reach it would undo that; routing
|
|
447
|
+
* it here keeps exactly one caller (`Session.hostExited`) and leaves the
|
|
448
|
+
* read surface untouched. The in-progress probe stays the caller's, so the
|
|
449
|
+
* store remains wire-shape-blind (INV-001).
|
|
450
|
+
*/
|
|
451
|
+
markEphemeralHostDeath(isInProgress: (item: Item) => boolean): readonly TerminalUnknownItemAnnotation[];
|
|
452
|
+
/**
|
|
453
|
+
* The session-state families, keyed by notification method name. Named
|
|
454
|
+
* `sessionState` rather than `state` because the surface contract reserves
|
|
455
|
+
* `state` for the FR-008 aggregate that arrives with snapshot ingestion.
|
|
456
|
+
* Snapshot-only, like `items`.
|
|
457
|
+
*/
|
|
458
|
+
get sessionState(): FoldSessionState;
|
|
459
|
+
/** The turn currently running, if one is. */
|
|
460
|
+
get activeTurnId(): string | undefined;
|
|
461
|
+
/**
|
|
462
|
+
* The outstanding delivery hole, or `undefined` when there is none (tdd
|
|
463
|
+
* SS4.8, FM-003).
|
|
464
|
+
*
|
|
465
|
+
* COALESCED, keeping the FIRST `after`: consecutive overflows are one hole
|
|
466
|
+
* from a filler's point of view, which is the client mirror of the server's
|
|
467
|
+
* own D-16487-1 bracket rule. A recovery walks `(after, next)` and calls
|
|
468
|
+
* `gapFilled(next)` with the target it actually reached, so a hole that grew
|
|
469
|
+
* while the walk was in flight cannot be reported filled.
|
|
470
|
+
*/
|
|
471
|
+
get pendingGap(): DeepReadonly<ViewGapParams> | undefined;
|
|
472
|
+
/**
|
|
473
|
+
* Is this fold current with the session view?
|
|
474
|
+
*
|
|
475
|
+
* FM-003's "only then reports current": false from the moment a `view/gap`
|
|
476
|
+
* folds until its hole is filled. Live events keep folding meanwhile — they
|
|
477
|
+
* are real facts and dropping them would add a second hole — so this flag is
|
|
478
|
+
* the ONLY statement that the transcript has a hole in it.
|
|
479
|
+
*/
|
|
480
|
+
get current(): boolean;
|
|
481
|
+
/**
|
|
482
|
+
* A recovery filled the hole up to `next`. Returns whether it cleared.
|
|
483
|
+
*
|
|
484
|
+
* EQUALITY, never a relational compare (tdd SS4.1): the argument is the
|
|
485
|
+
* target the walk reached, and it clears only when that is still the
|
|
486
|
+
* outstanding target. A gap that arrived mid-walk left a newer `next`, so
|
|
487
|
+
* this answers `false` and the fold stays not-current — the truthful answer,
|
|
488
|
+
* since the second hole is unfilled.
|
|
489
|
+
*
|
|
490
|
+
* A fold-level mutator with the same rationale as `markEphemeralHostDeath`:
|
|
491
|
+
* `items`/`sessionState` are snapshot-only allowlists, and this belongs to
|
|
492
|
+
* neither store.
|
|
493
|
+
*/
|
|
494
|
+
gapFilled(next: string): boolean;
|
|
495
|
+
/**
|
|
496
|
+
* Fold one view event.
|
|
497
|
+
*
|
|
498
|
+
* The `default` arm is not defensive padding: the wire is an external
|
|
499
|
+
* boundary and SS1.5.4 makes a NEW notification additive evolution, so a
|
|
500
|
+
* host newer than this SDK really does reach it. Ignoring the frame keeps
|
|
501
|
+
* the rest of the fold intact, which is what tolerance means. The second
|
|
502
|
+
* overload is how such a frame gets in: the wire's actual shape is
|
|
503
|
+
* `{ method: string, params }`, and the ONE tolerance cast lives here
|
|
504
|
+
* rather than at every call site — a hand-written literal for a KNOWN
|
|
505
|
+
* method should use the `ViewEvent` overload and keep its typo checking
|
|
506
|
+
* (PR #23087 review round).
|
|
507
|
+
*
|
|
508
|
+
* `params` is OPTIONAL on the wide overload because the generated
|
|
509
|
+
* `Notification` — the exact type this package's own `NotificationHandler`
|
|
510
|
+
* hands consumers — declares it optional. Requiring it here would make
|
|
511
|
+
* `fold.apply(notification)` a TS2769 and force every wiring site to
|
|
512
|
+
* re-wrap the frame, which is the ceremony this overload exists to remove.
|
|
513
|
+
*/
|
|
514
|
+
apply(event: ViewEvent): FoldOutcome;
|
|
515
|
+
apply(event: {
|
|
516
|
+
readonly method: string;
|
|
517
|
+
readonly params?: unknown;
|
|
518
|
+
}): FoldOutcome;
|
|
519
|
+
/** Turns in first-observed order. */
|
|
520
|
+
turns(): readonly DeepReadonly<TurnEntry>[];
|
|
521
|
+
turn(turnId: string): DeepReadonly<TurnEntry> | undefined;
|
|
522
|
+
/** Approvals awaiting a durable terminal, in first-observed order. */
|
|
523
|
+
pendingApprovals(): readonly DeepReadonly<PendingApproval>[];
|
|
524
|
+
/** The winning durable terminal per approval, in first-observed order. */
|
|
525
|
+
resolvedApprovals(): readonly DeepReadonly<ApprovalResolvedParams>[];
|
|
526
|
+
/**
|
|
527
|
+
* Prompts awaiting a durable settlement, in first-observed order. The
|
|
528
|
+
* entries ARE the generated request params — the wire shape already carries
|
|
529
|
+
* `userInputId`, so a wrapper would only duplicate it (unlike
|
|
530
|
+
* `PendingApproval`, which earns its wrapper with `latestUpdate`).
|
|
531
|
+
*/
|
|
532
|
+
pendingUserInputs(): readonly DeepReadonly<UserInputRequestParams>[];
|
|
533
|
+
/** The winning durable settlement per prompt, in first-observed order. */
|
|
534
|
+
settledUserInputs(): readonly DeepReadonly<UserInputSettledParams>[];
|
|
535
|
+
}
|
|
536
|
+
export {};
|