@hydraharness/harness-client-runtime 0.0.0-stage → 0.1.1-rc.7

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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -2
  3. package/lib/client.js +10964 -0
  4. package/lib/index.js +6 -0
  5. package/lib/invariant.js +36 -0
  6. package/lib/types/client/agents/scope.d.ts +35 -0
  7. package/lib/types/client/contract/conversation.d.ts +242 -0
  8. package/lib/types/client/contract/session.d.ts +97 -0
  9. package/lib/types/client/contract/sessions-port.d.ts +55 -0
  10. package/lib/types/client/contract/sessions.d.ts +164 -0
  11. package/lib/types/client/contract/settings-scope.d.ts +79 -0
  12. package/lib/types/client/contract/store.d.ts +90 -0
  13. package/lib/types/client/contract/workspaces.d.ts +102 -0
  14. package/lib/types/client/conversation/definition-registry.d.ts +30 -0
  15. package/lib/types/client/conversation/event-registry.d.ts +27 -0
  16. package/lib/types/client/conversation/view-registry.d.ts +15 -0
  17. package/lib/types/client/index.d.ts +128 -0
  18. package/lib/types/client/ordered-baseline.d.ts +12 -0
  19. package/lib/types/client/sessions/assistant-timing.d.ts +34 -0
  20. package/lib/types/client/sessions/context-provenance.d.ts +58 -0
  21. package/lib/types/client/sessions/conversation-assembler.d.ts +107 -0
  22. package/lib/types/client/sessions/conversation-context.d.ts +22 -0
  23. package/lib/types/client/sessions/conversation-location-index.d.ts +70 -0
  24. package/lib/types/client/sessions/conversation-versions.d.ts +25 -0
  25. package/lib/types/client/sessions/conversation.d.ts +422 -0
  26. package/lib/types/client/sessions/failure-display.d.ts +7 -0
  27. package/lib/types/client/sessions/lineage.d.ts +47 -0
  28. package/lib/types/client/sessions/manager.d.ts +300 -0
  29. package/lib/types/client/sessions/notifier.d.ts +35 -0
  30. package/lib/types/client/sessions/partial.d.ts +40 -0
  31. package/lib/types/client/sessions/pending.d.ts +55 -0
  32. package/lib/types/client/sessions/projection-store.d.ts +111 -0
  33. package/lib/types/client/sessions/provide.d.ts +79 -0
  34. package/lib/types/client/sessions/queue-mirror.d.ts +33 -0
  35. package/lib/types/client/sessions/remotes.d.ts +10 -0
  36. package/lib/types/client/sessions/request-inspection.d.ts +74 -0
  37. package/lib/types/client/sessions/service.d.ts +415 -0
  38. package/lib/types/client/sessions/session.d.ts +287 -0
  39. package/lib/types/client/sessions/steering-history.d.ts +23 -0
  40. package/lib/types/client/sessions/subagent-lineage.d.ts +24 -0
  41. package/lib/types/client/sessions/tool-call-tree.d.ts +45 -0
  42. package/lib/types/client/slots.d.ts +191 -0
  43. package/lib/types/client/time-zone.d.ts +8 -0
  44. package/lib/types/client/workspaces/manager.d.ts +182 -0
  45. package/lib/types/client/workspaces/path.d.ts +17 -0
  46. package/lib/types/client/workspaces/service.d.ts +179 -0
  47. package/lib/types/client/workspaces/workspace.d.ts +65 -0
  48. package/lib/types/index.d.ts +4 -0
  49. package/lib/types/invariant.d.ts +16 -0
  50. package/package.json +94 -3
@@ -0,0 +1,47 @@
1
+ import type { ConversationRevision, SessionVersionState, SessionId, SessionSummary } from '@hydraharness/harness-api-remotes/client';
2
+ import type { SessionProjectionMap } from '@hydraharness/harness-session-projection/types';
3
+ import type { PendingInteractionStatus } from './pending.ts';
4
+ /** Host list summary enriched with the latest mux-projected durable title. */
5
+ export interface TitledSessionSummary extends SessionSummary {
6
+ title?: string;
7
+ /** Current host-computed projection values for list consumers. */
8
+ projectionValues?: Readonly<Partial<SessionProjectionMap>>;
9
+ }
10
+ /** One flattened session-list row with lineage depth and live pending interaction. */
11
+ export interface SessionListEntry {
12
+ /** Session-local transcript paths and the selected version. */
13
+ versionState?: SessionVersionState | undefined;
14
+ sessionId: SessionId;
15
+ title?: string;
16
+ updatedAt: number;
17
+ running: boolean;
18
+ /** Empty-log bit mirrored from the summary; lists hide blank sessions (filtering stays with the consumer). */
19
+ blank: boolean;
20
+ parentSessionId?: SessionId;
21
+ /** Prompt revision identity retained for conversation navigation. */
22
+ revision?: ConversationRevision | undefined;
23
+ /** Coarse durable origin for navigation filtering; not a continuation capability. */
24
+ origin?: 'subagent';
25
+ cwd?: string;
26
+ /** Agent preset the session's agent was composed from (summary passthrough). */
27
+ agentPreset?: string;
28
+ /** Current host-computed projection values for list consumers. */
29
+ projectionValues?: Readonly<Partial<SessionProjectionMap>>;
30
+ /** User interaction currently blocking this session, derived from live mux frames. */
31
+ pendingInteraction?: PendingInteractionStatus;
32
+ /** Finished running while not selected and not yet opened — the sidebar's green "done" reminder (clears on select or the next run). */
33
+ completed: boolean;
34
+ /** Lineage indent depth: root = 0; the UI just multiplies by the indent width. */
35
+ depth: number;
36
+ }
37
+ /**
38
+ * Summaries -> flat list with lineage indentation. Root and sibling order
39
+ * follows the established input order; this projection never re-sorts a
40
+ * hydrated list from mutable timestamps.
41
+ * @param summaries - the host's session.list items.
42
+ * @param pendingInteractions - current manager-owned interaction status by session.
43
+ * @param completed - sessions with a pending completion reminder (manager-owned live fact; absent = false).
44
+ * @returns display rows in render order.
45
+ */
46
+ export declare function flattenLineage(summaries: readonly TitledSessionSummary[], pendingInteractions?: ReadonlyMap<SessionId, PendingInteractionStatus>, completed?: ReadonlySet<SessionId>): SessionListEntry[];
47
+ //# sourceMappingURL=lineage.d.ts.map
@@ -0,0 +1,300 @@
1
+ import type { IApiClient, HostFrame, MuxFrame, RpcError, RpcRequest, RpcResult, SessionId, SubagentAddress, SubagentCatalog, JobView, WorkspaceId } from '@hydraharness/harness-api-remotes/client';
2
+ import type { PromptRevisionRequest } from '@hydraharness/harness-host-apiproxy/api';
3
+ import type { ConversationRuntime } from './conversation-assembler.ts';
4
+ import type { SessionListEntry } from './lineage.ts';
5
+ import { Session } from './session.ts';
6
+ import type { SessionRemotes } from './remotes.ts';
7
+ /**
8
+ * List arrival lifecycle, orthogonal to the pull-activity `state` axis:
9
+ * `pending` (no successful pull yet — an empty items array means "nothing
10
+ * arrived", not "nothing exists") → `ready` (at least one pull landed).
11
+ * Monotone: `ready` never steps back — later pull failures and reconnect
12
+ * re-pulls ride the `state`/`error` axis, which is where failure is modeled
13
+ * (no `error` phase here; that would duplicate `state`).
14
+ */
15
+ export type SessionListPhase = 'pending' | 'ready';
16
+ /** Request-local content hit returned to sidebar search consumers. */
17
+ export interface SessionSearchResultItem {
18
+ sessionId: SessionId;
19
+ snippet: string;
20
+ }
21
+ /** Immutable session-list snapshot for useSessionList. */
22
+ export interface SessionListSnapshot {
23
+ items: readonly SessionListEntry[];
24
+ /** Selected Session id (validated against items; masked to undefined while its session is off the list). */
25
+ current: SessionId | undefined;
26
+ state: 'idle' | 'loading' | 'error';
27
+ /** Arrival lifecycle (see {@link SessionListPhase}); `state` stays the pull-activity axis. */
28
+ phase: SessionListPhase;
29
+ error: RpcError | null;
30
+ subagentsByParent: Readonly<Record<SessionId, SubagentCatalogSnapshot>>;
31
+ /** Background jobs per session; an absent key is an empty set. */
32
+ jobsBySession: Readonly<Record<SessionId, readonly JobView[]>>;
33
+ currentAddress: SubagentAddress | undefined;
34
+ }
35
+ /** One parent-addressed durable catalog projected through the sessions snapshot. */
36
+ export interface SubagentCatalogSnapshot extends SubagentCatalog {
37
+ state: 'loading' | 'ready' | 'error';
38
+ error: RpcError | null;
39
+ }
40
+ /** Instance cluster + frame entry + the session list. */
41
+ export declare class SessionManager {
42
+ private readonly api;
43
+ private readonly remote;
44
+ private readonly conversation?;
45
+ private readonly sessions;
46
+ /** Pre-instantiation buffer for answerable requests and the queued-turn snapshot, which history
47
+ * cannot reconstruct on open. Live requests remain until resolution; queue and replay duplicates
48
+ * compact by identity. Instantiation replays and clears it, while removal drops it. */
49
+ private readonly pendingBuffers;
50
+ /** Outstanding answerable interactions per session, keyed by their stable request identity.
51
+ * Manager-owned rather than read off Session instances because the sidebar must light up for
52
+ * sessions never instantiated. Cleared per connection generation — the reopen replay re-adds
53
+ * still-pending requests — and on session-removed. */
54
+ private readonly pendingInteractions;
55
+ /**
56
+ * Sessions that finished running while not selected — the sidebar's green
57
+ * "done" reminder (manager-owned, survives connection generations; cleared
58
+ * on select and session-removed, re-armed by the next completion).
59
+ */
60
+ private readonly completedNotifications;
61
+ /** Last-observed running bits per session; the true→false edge here arms {@link completedNotifications}. */
62
+ private readonly prevRunning;
63
+ /** Per-session projection value stores, retained independently of instance arrival (the
64
+ * title-snapshot precedent, generalized): push frames land here whether or not the Session
65
+ * is instantiated (list rows read the 'title' key), and an instantiated Session adopts the
66
+ * same store so history-baseline seeding and frames converge on one row set. */
67
+ private readonly projectionStores;
68
+ private summaries;
69
+ private listState;
70
+ /** Arrival phase; the pending → ready edge fires on the first successful pull (see SessionListPhase). */
71
+ private listPhase;
72
+ private listError;
73
+ private listInflight;
74
+ /** Mutations arriving after a list request starts are replayed over its response. */
75
+ private listMutations;
76
+ private readonly addresses;
77
+ private readonly catalogs;
78
+ private readonly catalogInflight;
79
+ /** Catalog owners whose membership changed while a pull was in flight: one trailing refresh after it settles. */
80
+ private readonly catalogStale;
81
+ private readonly openCatalogs;
82
+ private readonly catalogDebounce;
83
+ /**
84
+ * Background jobs per session, last-wins from `session/jobs`. An empty set
85
+ * is stored as an absent key, so absence and `[]` are one representation.
86
+ */
87
+ private readonly jobsBySession;
88
+ private selected;
89
+ private listSnapshotCache;
90
+ /** Entry-identity cache (reference stability): list rebuilds reuse the previous entry
91
+ * object when every field matches — wire refreshes mint all-new summary objects, so identity
92
+ * must be recovered by value or every SessionListItem memo misses on every refresh. */
93
+ private entryCache;
94
+ private itemsCache;
95
+ private readonly notifier;
96
+ /**
97
+ * @param api - shared wire client.
98
+ * @param restoredSelection - persisted real-Session selection candidate.
99
+ */
100
+ constructor(api: IApiClient, remote: SessionRemotes, restoredSelection?: SessionId, restoredAddress?: SubagentAddress, conversation?: ConversationRuntime | undefined);
101
+ /**
102
+ * Select a listed Session or a retained catalog-addressed child.
103
+ * @param sessionId - listed or catalog-addressed Session id.
104
+ */
105
+ select(sessionId: SessionId): void;
106
+ /**
107
+ * Select a healthy child through its durable direct-parent address.
108
+ * @param address - catalog-derived parent and child ids.
109
+ */
110
+ selectSubagent(address: SubagentAddress): void;
111
+ /** Clear the selection (the layout falls to the no-session view state). */
112
+ clearSelection(): void;
113
+ /**
114
+ * Return the durable catalog address retained for one child.
115
+ * @param sessionId - possible addressed child id.
116
+ * @returns The direct-parent address, when navigation discovered one.
117
+ */
118
+ subagentAddress(sessionId: SessionId): SubagentAddress | undefined;
119
+ /**
120
+ * Resolve an address for breadcrumb navigation without retaining transport authority.
121
+ * @param sessionId - possible child id in an already-loaded catalog.
122
+ * @returns A retained or catalog-derived direct-parent address.
123
+ */
124
+ navigationAddress(sessionId: SessionId): SubagentAddress | undefined;
125
+ /**
126
+ * Drop a session instance (scope-prune companion: instance
127
+ * and scope share one lifecycle). The host session log is the durable
128
+ * truth — a later get() lazily rebuilds and open() backfills history.
129
+ * @param sessionId - the session to drop.
130
+ */
131
+ drop(sessionId: SessionId): void;
132
+ /**
133
+ * Lazy build: return the existing instance or construct one (no auto-open —
134
+ * open is triggered by the container's select callback).
135
+ * @param sessionId - the session to get.
136
+ * @returns the resident instance.
137
+ */
138
+ get(sessionId: SessionId): Session;
139
+ private createSession;
140
+ /** Rebuild every resident Session after one coalesced registry transaction. */
141
+ rebuildConversationRegistry(): void;
142
+ /** Resident per-session projection store (create-on-demand; outlives instantiation). */
143
+ private projectionStore;
144
+ /**
145
+ * Refresh one direct-child catalog, reusing its in-flight request.
146
+ * @param parentSessionId - catalog owner.
147
+ */
148
+ refreshSubagents(parentSessionId: SessionId): Promise<void>;
149
+ /**
150
+ * Mark whether a catalog menu is consuming live membership updates.
151
+ * @param parentSessionId - catalog owner.
152
+ * @param open - current menu state.
153
+ */
154
+ setSubagentCatalogOpen(parentSessionId: SessionId, open: boolean): void;
155
+ /** Full refresh via session.list (single-flight: an in-flight call is reused). */
156
+ refreshList(): Promise<void>;
157
+ /**
158
+ * Search visible session message content without adding transient query
159
+ * state to the list snapshot.
160
+ * @param query - non-blank literal phrase.
161
+ * @param signal - cancellation for superseded UI queries.
162
+ * @returns the Host result or a folded transport error.
163
+ */
164
+ search(query: string, signal: AbortSignal): Promise<RpcResult<{
165
+ items: SessionSearchResultItem[];
166
+ hasMore: boolean;
167
+ }>>;
168
+ /**
169
+ * Contract session.create; on success merge into summaries immediately (no
170
+ * wait for the next refresh). A created session is blank by definition
171
+ * (entity birth precedes the first message).
172
+ * @param opts - target workspace or working directory, plus an optional caller-owned id.
173
+ * @returns the create result.
174
+ */
175
+ create(opts?: {
176
+ workspaceId?: WorkspaceId;
177
+ cwd?: string;
178
+ sessionId?: SessionId;
179
+ reuseWorkspaceBlank?: true;
180
+ }): Promise<RpcResult<{
181
+ sessionId: SessionId;
182
+ }>>;
183
+ /**
184
+ * Permanently delete a session and remove it from the local projection.
185
+ * @param sessionId - session and conversation identity to delete.
186
+ * @returns the Host result, including every affected session id.
187
+ */
188
+ delete(sessionId: SessionId): Promise<RpcResult<{
189
+ deleted: true;
190
+ sessionIds: SessionId[];
191
+ }>>;
192
+ /**
193
+ * Contract session.fork; on success merge the child into summaries
194
+ * immediately (same synchronous-addressability guarantee as create). The
195
+ * child carries the source's history, so it is never blank; lineage rides
196
+ * parentSessionId so the list nests it under its source. A child published
197
+ * before Workspace attachment fails is also reconciled into the list.
198
+ * @param opts - source session and the optional seq anchoring the cut.
199
+ * @returns the fork result (the child session id).
200
+ */
201
+ fork(opts: {
202
+ sessionId: SessionId;
203
+ atSeq?: number;
204
+ beforeSeq?: number;
205
+ }): Promise<RpcResult<{
206
+ sessionId: SessionId;
207
+ }>>;
208
+ /**
209
+ * Admit one immutable edit/retry and publish its Host-confirmed summary.
210
+ * @param input - Source address and caller-owned idempotency key.
211
+ * @returns durable generation attempt identity or the original RPC error.
212
+ */
213
+ revise(input: PromptRevisionRequest): Promise<RpcResult<{
214
+ sessionId: SessionId;
215
+ }>>;
216
+ /**
217
+ * View or reference a stored path without creating another Session object.
218
+ * @param sessionId - Owning session.
219
+ * @param versionId - Stored path.
220
+ * @param mode - View or reference operation.
221
+ * @returns Host acknowledgement and refreshed catalog.
222
+ */
223
+ selectVersion(sessionId: SessionId, versionId: import('@hydraharness/harness-session/types').SessionVersionId, mode: 'view' | 'reference'): Promise<void>;
224
+ /**
225
+ * Insert-or-enrich a locally synthesized summary: a new id prepends; an
226
+ * existing entry only gains fields it lacks (the session-added frame and the
227
+ * create() echo race — whichever lands second must fill the placeholder's
228
+ * missing cwd/parentSessionId, never overwrite list-refresh data).
229
+ */
230
+ private mergeSummary;
231
+ /**
232
+ * Record a host-confirmed composition switch (see ISessions.noteAgentPreset).
233
+ * @param sessionId - the switched session.
234
+ * @param agentPreset - the preset id the host confirmed.
235
+ */
236
+ noteAgentPreset(sessionId: SessionId, agentPreset: string): void;
237
+ /** Apply immediately and retain for replay when a list response is in flight. */
238
+ private recordMutation;
239
+ /**
240
+ * uSES subscription entry for useSessionList.
241
+ * @param listener - change callback.
242
+ * @returns the unsubscribe function.
243
+ */
244
+ subscribe(listener: () => void): () => void;
245
+ /**
246
+ * Cached list snapshot (rebuilt lazily when dirty with no listeners).
247
+ * @returns the cached reference (stable until the next flush).
248
+ */
249
+ getListSnapshot(): SessionListSnapshot;
250
+ /** Add or refresh one stable pending-interaction identity. */
251
+ private trackPending;
252
+ /** Settle one pending-interaction identity without disturbing sibling waits. */
253
+ private resolvePending;
254
+ /**
255
+ * Mux frame entry: sessionId-bearing frames go only to instantiated sessions
256
+ * (no lazy build; non-pending frames for uninstantiated sessions drop —
257
+ * history backfills them on open).
258
+ * @param envelope - the frame with its wire rpcId.
259
+ */
260
+ handleMuxEnvelope(envelope: RpcRequest<MuxFrame>): void;
261
+ /**
262
+ * Host frame entry: list upkeep + per-instance running/removed/agent-error relay.
263
+ * @param envelope - the frame with its wire rpcId.
264
+ */
265
+ handleHostEnvelope(envelope: RpcRequest<HostFrame>): void;
266
+ private handleHostFrame;
267
+ /**
268
+ * The moment a connection generation dies (before any next-generation frame
269
+ * can arrive — onConnected waits for the readiness handshake while replayed
270
+ * frames flow from stream open, so clearing there would race the replay):
271
+ * drop generation-scoped live state. Interactions resolved while disconnected
272
+ * send no frame, so stale statuses and buffered answerable frames must not
273
+ * survive into the next generation — mux-open replay re-adds every still-pending
274
+ * request with its live rpcId.
275
+ */
276
+ handleDisconnected(): void;
277
+ /** After each connection generation: refresh the session baseline and rebuild opened windows. */
278
+ handleConnected(): void;
279
+ /** Debounce membership refetches while one parent catalog is selected or open. */
280
+ private scheduleCatalogRefresh;
281
+ /** Apply one Agent-driver transition to loaded and in-flight catalogs. */
282
+ private updateCatalogActivity;
283
+ /** Preserve and project a positive expandability hint after one direct subagent publishes. */
284
+ private markCatalogParentExpandable;
285
+ /** Apply one positive expandability hint to every loaded catalog containing that unique row id. */
286
+ private applyCatalogParentExpandable;
287
+ /** Fold request-local row mutations into one catalog result before publication. */
288
+ private withCatalogMutations;
289
+ /**
290
+ * Reconcile completion reminders against the latest summaries, eagerly after
291
+ * every mutation and pull (a snapshot-build-time pass would collapse
292
+ * consecutive status frames into one observation). A running→idle edge of a
293
+ * non-selected session arms its reminder; running disarms it; removal drops
294
+ * it. First observation only records the running bit — sessions already
295
+ * idle at load get no reminder.
296
+ */
297
+ private syncCompletedNotifications;
298
+ private buildListSnapshot;
299
+ }
300
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1,35 @@
1
+ /** Subscription + batched notification primitive (shared by Session and SessionManager). */
2
+ export declare class Notifier {
3
+ private readonly rebuild;
4
+ private listeners;
5
+ private dirty;
6
+ private notifyPending;
7
+ private scheduled;
8
+ private scheduleGeneration;
9
+ /** @param rebuild - snapshot rebuild function injected by the owner (writes the owner's snapshotCache). */
10
+ constructor(rebuild: () => void);
11
+ /**
12
+ * uSES subscription entry.
13
+ * @param listener - change callback.
14
+ * @returns the unsubscribe function.
15
+ */
16
+ subscribe(listener: () => void): () => void;
17
+ /** State-change entry: mark dirty and schedule the batched flush. */
18
+ markDirty(): void;
19
+ /** Stream-change entry: mark dirty and publish the cumulative state at most once per frame. */
20
+ markFrameDirty(): void;
21
+ /**
22
+ * Synchronous flush: controlled-input writes must notify in the same tick as
23
+ * onChange, or React rolls the DOM back to the stale value and the caret jumps to the end.
24
+ */
25
+ notifyNow(): void;
26
+ /**
27
+ * Pre-getSnapshot check: rebuild synchronously when dirty (read path
28
+ * before first subscribe / while unobserved). Notification stays pending.
29
+ */
30
+ ensureFresh(): void;
31
+ private schedule;
32
+ private invalidateSchedule;
33
+ private flush;
34
+ }
35
+ //# sourceMappingURL=notifier.d.ts.map
@@ -0,0 +1,40 @@
1
+ import type { StreamChunk } from '@hydraharness/harness-llm/types';
2
+ import type { AssistantBlock, PartialAssistant } from './conversation.ts';
3
+ /**
4
+ * Whether a stream chunk changes the partial assistant projection shown by the UI.
5
+ * @param type - Stream chunk discriminant.
6
+ * @returns Whether publishing the accumulated partial can change the visible snapshot.
7
+ */
8
+ export declare function isVisibleAssistantChunk(type: string): boolean;
9
+ /** assistant/chunk accumulator: folds StreamChunks into AssistantBlock[] with block-level immutability. */
10
+ export declare class PartialAccumulator {
11
+ readonly turn: number;
12
+ readonly step: number;
13
+ private blocks;
14
+ private changed;
15
+ private snapshot;
16
+ /**
17
+ * @param turn - Owning agent turn.
18
+ * @param step - Owning model step.
19
+ * @param initialBlocks - Materialized prefix when accumulation begins after history replay.
20
+ */
21
+ constructor(turn: number, step: number, initialBlocks?: readonly AssistantBlock[]);
22
+ /**
23
+ * Fold one chunk.
24
+ * @param chunk - the stream chunk.
25
+ * @returns whether it caused a visible change (usage/finish return false, skipping notification).
26
+ */
27
+ push(chunk: StreamChunk): boolean;
28
+ /**
29
+ * Current partial projection.
30
+ * @returns the cached snapshot (the blocks array reference only changes after a mutation).
31
+ */
32
+ toPartial(): PartialAssistant;
33
+ }
34
+ /**
35
+ * Create the empty client projection for one streamed Assistant block kind.
36
+ * @param blockType - wire block kind.
37
+ * @returns empty projected block ready to receive deltas.
38
+ */
39
+ export declare function emptyAssistantBlock(blockType: string): AssistantBlock;
40
+ //# sourceMappingURL=partial.d.ts.map
@@ -0,0 +1,55 @@
1
+ import type { ClientResponse, MuxFrame, RpcId, RpcReceipt, SessionId } from '@hydraharness/harness-api-remotes/client';
2
+ /** Kind-keyed payload map: the requested frame's domain fields (envelope fields stripped). */
3
+ export interface PendingPayloads {
4
+ approval: Omit<Extract<MuxFrame, {
5
+ type: 'approval/requested';
6
+ }>, 'type' | 'sessionId'>;
7
+ question: Omit<Extract<MuxFrame, {
8
+ type: 'question/requested';
9
+ }>, 'type' | 'sessionId'>;
10
+ }
11
+ /** Pending-interaction discriminant (the keys of PendingPayloads). */
12
+ export type PendingKind = keyof PendingPayloads;
13
+ /** Session-list summary of the user action currently blocking progress. */
14
+ export type PendingInteractionStatus = 'approval' | 'plan-review' | 'question';
15
+ /** Kind-discriminated union of concrete waits: narrowing on `kind` types `payload`. */
16
+ export type PendingInteraction = {
17
+ [K in PendingKind]: PendingWait<K>;
18
+ }[PendingKind];
19
+ /**
20
+ * One pending host-owned interaction wait: an immutable render face
21
+ * (kind/key/sessionId/payload) plus the response carrier. respond() backfills
22
+ * the requested frame's rpcId into a client-response envelope — no consumer
23
+ * ever sees the raw rpcId. Settlement is expressed only by pending-list
24
+ * membership (the settled flag is a fail-loud guard, not a render input).
25
+ */
26
+ export declare class PendingWait<K extends PendingKind = PendingKind> {
27
+ #private;
28
+ /** Interaction kind (union discriminant). */
29
+ readonly kind: K;
30
+ /** Opaque render identity, `<prefix>:<rpcId>` — stable across baseline replay, usable as a React key. */
31
+ readonly key: string;
32
+ /** Owning session. */
33
+ readonly sessionId: SessionId;
34
+ /** The requested frame's domain fields, verbatim. */
35
+ readonly payload: PendingPayloads[K];
36
+ /**
37
+ * Minted by Session on a requested frame (public construction is the test-fixture path).
38
+ * @param kind - interaction kind.
39
+ * @param rpcId - the requested frame's stable envelope id (kept private; respond echoes it).
40
+ * @param sessionId - owning session.
41
+ * @param payload - the requested frame's domain fields.
42
+ * @param respond - the client-response carrier (api.respond).
43
+ */
44
+ constructor(kind: K, rpcId: RpcId, sessionId: SessionId, payload: PendingPayloads[K], respond: (message: ClientResponse) => Promise<RpcReceipt>);
45
+ /**
46
+ * Send a result for this wait: wraps it into the client-response envelope
47
+ * with the rpcId backfilled. Throws synchronously once settled.
48
+ * @param result - the result shell (ok value / error envelope), domain-encoded by the caller.
49
+ * @returns the carrier receipt.
50
+ */
51
+ respond(result: ClientResponse['result']): Promise<RpcReceipt>;
52
+ /** Session-only settlement mark (the authoritative resolved frame arrived); respond() throws afterwards. */
53
+ markSettled(): void;
54
+ }
55
+ //# sourceMappingURL=pending.d.ts.map
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Generic per-session projection value store (push model; see the
3
+ * session-projection subsystem page, docs/subsystems/session-projection.md):
4
+ * the host is the only computation site; the client holds finished
5
+ * whole values per key — `key → { value, seq }` — seeded by the history tail
6
+ * page's projections block and updated by `session/projection` push frames,
7
+ * under the single rule **higher seq wins**. No client-side domain folding
8
+ * exists: a domain ships projection support with zero client code. Per-key
9
+ * bare observable faces feed `useProjection` (ui-renderer binds them).
10
+ */
11
+ import type { SessionProjectionMap } from '@hydraharness/harness-session-projection/types';
12
+ import type { ObservableSnapshot } from '../contract/store.ts';
13
+ export type { SessionProjectionMap } from '@hydraharness/harness-session-projection/types';
14
+ /**
15
+ * The fifth framework hook seat (see the session-projection subsystem page,
16
+ * docs/subsystems/session-projection.md): key-addressed
17
+ * projection reader delivered through the standard kit. `undefined` uniformly
18
+ * means capability absent — host unit unmounted, or no baseline/frame has
19
+ * carried the key yet. The selector overload mirrors useSession (per-key uSES
20
+ * binding; reference stability holds because a key's value reference changes
21
+ * only when a frame or baseline lands).
22
+ */
23
+ export type UseProjection = {
24
+ <K extends Extract<keyof SessionProjectionMap, string>>(key: K): SessionProjectionMap[K] | undefined;
25
+ <K extends Extract<keyof SessionProjectionMap, string>, S>(key: K, selector: (value: SessionProjectionMap[K] | undefined) => S, eq?: (a: S, b: S) => boolean): S;
26
+ };
27
+ /**
28
+ * Tail-page projections baseline — structurally identical to the wire's
29
+ * `SessionProjectionsBlock` (apiproxy api layer), restated here so the
30
+ * React-free store depends only on the type table, not the wire package's
31
+ * response vocabulary.
32
+ */
33
+ export interface ProjectionsBaseline {
34
+ /** The consistent-cut seq (equals the window tail seq by construction). */
35
+ asOfSeq: number;
36
+ /** Whole current values by key; a registered key absent here means the capability is absent. */
37
+ values: Partial<SessionProjectionMap>;
38
+ }
39
+ /**
40
+ * One session's projection values. Framework semantics, uniform across every
41
+ * key: a baseline seeds rows at its cut, a push frame updates one row, and in
42
+ * both paths a lower-or-equal seq loses — a replayed frame cannot regress a
43
+ * value, a stale baseline cannot overwrite a newer frame. A key the store has
44
+ * never seen reads `undefined` (capability absent). Faces are identity-stable
45
+ * per key (create-on-demand, cached) so the React side binds each exactly
46
+ * once; the store-level channel (`subscribeAny`) serves coarse consumers (the
47
+ * manager's list projection reads the `title` key).
48
+ */
49
+ export declare class ProjectionValueStore {
50
+ private readonly rows;
51
+ private readonly channels;
52
+ private valuesCache;
53
+ /** Coarse any-key channel (no snapshot cache to rebuild: reads hit rows directly). */
54
+ private readonly anyNotifier;
55
+ /**
56
+ * Key-addressed bare observable face (the useProjection resolution path).
57
+ * Always defined — absence is an `undefined` snapshot, never a missing
58
+ * face, so a component may subscribe before the key ever carries a value.
59
+ * @param key - projection key.
60
+ * @returns the identity-stable face for this key.
61
+ */
62
+ faceOf(key: string): ObservableSnapshot<unknown>;
63
+ /**
64
+ * Current whole value for a key (erased framework read; typed reads go
65
+ * through `useProjection`'s map lookup).
66
+ * @param key - projection key.
67
+ * @returns the value, or undefined while the key is absent.
68
+ */
69
+ get(key: string): unknown;
70
+ /**
71
+ * Read every current projection value as one reference-stable snapshot.
72
+ * @returns The same frozen value map until a row changes.
73
+ */
74
+ values(): Readonly<Partial<SessionProjectionMap>>;
75
+ /**
76
+ * Subscribe to any-key changes (microtask-batched) — the manager's list
77
+ * rebuild channel.
78
+ * @param listener - change callback.
79
+ * @returns the unsubscribe function.
80
+ */
81
+ subscribeAny(listener: () => void): () => void;
82
+ /**
83
+ * Apply one finished value (the `session/projection` push-frame path).
84
+ * @param key - projection key.
85
+ * @param value - whole value computed by the host unit.
86
+ * @param seq - the unit's watermark at emission.
87
+ */
88
+ apply(key: string, value: unknown, seq: number): void;
89
+ /**
90
+ * Seed from a history tail page's projections block: every carried key
91
+ * lands under the same seq rule as frames; a key the block omits is
92
+ * capability-absent as of the cut — its row clears unless a newer frame
93
+ * already superseded the cut (a stale baseline can neither overwrite nor
94
+ * clear newer values).
95
+ * @param baseline - the response's projections block.
96
+ */
97
+ seed(baseline: ProjectionsBaseline): void;
98
+ /**
99
+ * Drop rows past a mux-generation baseline (`session/subscribed.lastSeq`):
100
+ * a row claiming knowledge beyond the host's own durable baseline rode
101
+ * state a restart lost — under last-wins it would wrongly outrank the
102
+ * host's recomputed (lower-seq) values forever. Durable replay and the next
103
+ * baseline re-seed whatever truly survived (the title-snapshot precedent,
104
+ * generalized).
105
+ * @param lastSeq - the subscribed frame's durable baseline seq.
106
+ */
107
+ truncate(lastSeq: number): void;
108
+ private changed;
109
+ private channel;
110
+ }
111
+ //# sourceMappingURL=projection-store.d.ts.map
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The session standard-props provide channel: provider roster, bundle
3
+ * materialization (fail-loud on undeclared/missing/duplicate members), the
4
+ * static no-session projection, and the atomic current-session projection
5
+ * observable. One implementation — SessionRuntime drives it from wire
6
+ * truth, the test runtime's sessions double drives it from fixtures — so
7
+ * the materialization rules and the projection semantics cannot drift
8
+ * between production and the test bench.
9
+ */
10
+ import type { HostObservable, SessionMaybeProvideInfo, SessionProvideInfo } from '@hydraharness/harness-client-ui-slots';
11
+ import type { SessionBinding, SessionProvideDescriptor } from './service.ts';
12
+ /** The owner-side hooks: how the channel reaches the owner's live bundles and current selection. */
13
+ export interface SessionProvideChannelHost {
14
+ /**
15
+ * Re-materialize every already-materialized bundle against the new roster
16
+ * (call {@link SessionProvideChannel.materializeInfo} per live binding).
17
+ * Lazily-materialized sessions pick the new roster up on first resolve.
18
+ */
19
+ rebuildBundles(): void;
20
+ /** Resolve the current selection's bundle (the owner's maybe-provide lookup). */
21
+ resolveCurrent(): SessionMaybeProvideInfo;
22
+ }
23
+ /**
24
+ * Provider roster + materialization + current projection. The channel owns
25
+ * every rule a provider contribution must satisfy; owners keep only their
26
+ * per-session bundle storage and the definition of "current".
27
+ */
28
+ export declare class SessionProvideChannel {
29
+ private readonly host;
30
+ private readonly providers;
31
+ private maybeInfoCache;
32
+ /** Latest published current bundle (identity comparison dedupes republish). */
33
+ private currentSnapshot;
34
+ /** Projection subscribers (plain cell: bundles hold live session sources, so no store freeze may touch them). */
35
+ private readonly listeners;
36
+ /**
37
+ * Atomic current-session provide projection: selection changes and
38
+ * provider-roster changes publish through this one source, so a roster
39
+ * change under a stable current id republishes the bundle instead of
40
+ * stranding mounted entries.
41
+ */
42
+ readonly currentProvideInfo: HostObservable<SessionMaybeProvideInfo>;
43
+ /**
44
+ * @param host - owner-side bundle storage and current-selection resolution.
45
+ */
46
+ constructor(host: SessionProvideChannelHost);
47
+ /** The static no-session projection under the current roster (declared names present, values undefined). */
48
+ get maybeInfo(): SessionMaybeProvideInfo;
49
+ /**
50
+ * Register a per-session standard-props provider (see
51
+ * SessionRuntime.provide for the product contract). Live bundles rebuild
52
+ * immediately; misdeclared providers fail loud here, at the registration
53
+ * edge, and the registration rolls back — the channel never stays on a
54
+ * roster it cannot materialize.
55
+ * @param descriptor - static member roster plus per-session resolver.
56
+ * @returns disposer removing the provider.
57
+ */
58
+ provide(descriptor: SessionProvideDescriptor): () => void;
59
+ /**
60
+ * Re-derive the current selection's bundle and publish it when it changed.
61
+ * Bundles are identity-stable per (scope, roster) materialization, so an
62
+ * identity compare is exact; synchronous notify — call sites (the owner's
63
+ * list subscription, provide()) already sit behind their own batching or
64
+ * registration edges.
65
+ */
66
+ publishCurrent(): void;
67
+ /**
68
+ * Materialize the standard-props bundle for one session (fails loud on
69
+ * undeclared, missing, and duplicate member names).
70
+ * @param binding - session assembly handle fed to every resolver.
71
+ * @returns the materialized bundle (identity-stable until the next materialization).
72
+ */
73
+ materializeInfo(binding: SessionBinding): SessionProvideInfo;
74
+ /** Rebuild the static projection and the owner's live bundles, then republish the current one. */
75
+ private applyRosterChange;
76
+ /** Build the static no-session kit and reject duplicate declared names. */
77
+ private materializeMaybeInfo;
78
+ }
79
+ //# sourceMappingURL=provide.d.ts.map