@semiont/api-client 0.4.19 → 0.4.21

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/dist/index.d.ts CHANGED
@@ -1,272 +1,57 @@
1
- import * as _semiont_core from '@semiont/core';
2
- import { BaseUrl, Logger, ResourceId, AccessToken, components, UserDID, paths, AnnotationId, GraphConnection, UpdateResourceInput, Motivation, MarkProgress, BodyOperation, GatheredContext, YieldProgress as YieldProgress$1, JobId, EventBus, Email, RefreshToken, GoogleCredential, ContentFormat, SearchQuery, CloneToken, EntityType } from '@semiont/core';
1
+ import { components, UserDID, paths, ResourceId, AnnotationId, GraphConnection, UpdateResourceInput, Motivation, BodyOperation, GatheredContext, JobId, EventBus, AccessToken, BaseUrl, Logger, EventMap, Email, RefreshToken, GoogleCredential, ContentFormat, SearchQuery, CloneToken, EntityType, Selector } from '@semiont/core';
3
2
  export { Logger, Selector, getFragmentSelector, getSvgSelector, getTextPositionSelector, validateSvgMarkup } from '@semiont/core';
4
- import { Observable } from 'rxjs';
3
+ import { Observable, BehaviorSubject, Subject } from 'rxjs';
5
4
  export { BoundingBox, ContentCache, FragmentSelector, JWTTokenSchema, LOCALES, LocaleInfo, MatchQuality, Point, SvgSelector, TextPosition, TextPositionSelector, TextQuoteSelector, ValidatedAnnotation, ValidationFailure, ValidationResult, ValidationSuccess, buildContentCache, createCircleSvg, createPolygonSvg, createRectangleSvg, decodeRepresentation, decodeWithCharset, extractBoundingBox, extractCharset, extractContext, findBestTextMatch, findTextWithContext, formatLocaleDisplay, getAllLocaleCodes, getAnnotationExactText, getBodySource, getBodyType, getChecksum, getCommentText, getCreator, getDerivedFrom, getExactText, getLanguage, getLocaleEnglishName, getLocaleInfo, getLocaleNativeName, getNodeEncoding, getPrimaryMediaType, getPrimaryRepresentation, getPrimarySelector, getResourceEntityTypes, getResourceId, getStorageUri, getTargetSelector, getTargetSource, getTextQuoteSelector, hasTargetSelector, isArchived, isAssessment, isBodyResolved, isComment, isDraft, isHighlight, isReference, isResolvedReference, isStubReference, isTag, isValidEmail, normalizeCoordinates, normalizeText, parseSvgSelector, scaleSvgToNative, validateAndCorrectOffsets, validateData, verifyPosition } from './utils/index.js';
6
5
 
7
- /**
8
- * TypeScript types for SSE event payloads.
9
- *
10
- * Progress and result types for events delivered via the long-lived
11
- * events-stream. These match the payloads emitted by job workers on
12
- * the resource-scoped EventBus. Used by the MCP server for type-safe
13
- * event handling.
14
- */
15
- /**
16
- * Progress event for reference/linking detection stream
17
- *
18
- * Sent by POST /resources/:id/annotate-references
19
- *
20
- * @example
21
- * ```typescript
22
- * stream.onProgress((progress: ReferenceDetectionProgress) => {
23
- * if (progress.status === 'scanning') {
24
- * console.log(`Scanning for ${progress.currentEntityType}...`);
25
- * console.log(`Progress: ${progress.processedEntityTypes}/${progress.totalEntityTypes}`);
26
- * }
27
- * });
28
- * ```
29
- */
30
- interface ReferenceDetectionProgress {
31
- /** Current status of detection operation */
32
- status: 'started' | 'scanning' | 'complete' | 'error';
33
- /** Resource ID being scanned */
34
- resourceId: string;
35
- /** Currently scanning for this entity type (only present during 'scanning') */
36
- currentEntityType?: string;
37
- /** Total number of entity types to scan */
38
- totalEntityTypes: number;
39
- /** Number of entity types processed so far */
40
- processedEntityTypes: number;
41
- /** Human-readable status message */
42
- message?: string;
43
- /** Total entities found (only present in 'complete') */
44
- foundCount?: number;
45
- }
46
- /**
47
- * Progress event for resource generation stream
48
- *
49
- * Sent by POST /resources/:resourceId/annotations/:annotationId/yield-resource
50
- *
51
- * @example
52
- * ```typescript
53
- * stream.onProgress((progress: YieldProgress) => {
54
- * console.log(`${progress.status}: ${progress.percentage}%`);
55
- * console.log(progress.message);
56
- * });
57
- * ```
58
- */
59
- interface YieldProgress {
60
- /** Current stage of generation operation */
61
- status: 'started' | 'fetching' | 'generating' | 'creating' | 'complete' | 'error';
62
- /** Annotation ID being used as source */
63
- referenceId: string;
64
- /** Name of resource being generated */
65
- resourceName?: string;
66
- /** ID of generated resource (only present in 'complete') */
67
- resourceId?: string;
68
- /** ID of source resource */
69
- sourceResourceId?: string;
70
- /** Percentage complete (0-100) */
71
- percentage: number;
72
- /** Human-readable status message */
73
- message?: string;
74
- }
75
- /**
76
- * Resource event from real-time event stream
77
- *
78
- * Sent by GET /resources/:id/events/stream
79
- *
80
- * This represents a single event from the event store, broadcast in real-time
81
- * as it occurs. Used for real-time collaboration - multiple users see each
82
- * other's changes as they happen.
83
- *
84
- * Re-exported from @semiont/core (authoritative source).
85
- * The discriminated union type provides type-safe event handling.
86
- *
87
- * @example
88
- * ```typescript
89
- * stream.onEvent((event) => {
90
- * console.log(`Event: ${event.type}`);
91
- * console.log(`User: ${event.userId}`);
92
- * console.log(`Payload:`, event.payload);
93
- * });
94
- * ```
95
- */
96
- /**
97
- * SSE stream controller interface
98
- *
99
- * Returned by all SSE methods. Events auto-emit to EventBus (required).
100
- *
101
- * **Architecture Note**: All SSE methods require `eventBus` in options to enforce
102
- * event-driven architecture. This is enforced at compile time via TypeScript.
103
- *
104
- * @example
105
- * ```typescript
106
- * const eventBus = new EventBus();
107
- *
108
- * // Subscribe to events
109
- * eventBus.get('detection:progress').subscribe((p) => console.log(p.message));
110
- * eventBus.get('detection:complete').subscribe(() => console.log('Done!'));
111
- * eventBus.get('detection:failed').subscribe(({ error }) => console.error(error));
112
- *
113
- * // Start stream - eventBus is required (TypeScript enforced)
114
- * const stream = client.sse.markReferences(resourceId, request, { auth, eventBus });
115
- *
116
- * // Cleanup when done
117
- * stream.close();
118
- * ```
119
- */
120
- interface SSEStream {
121
- /**
122
- * Close the SSE stream and abort the connection
123
- *
124
- * Should be called to cleanup resources when stream is no longer needed.
125
- * Safe to call multiple times.
126
- *
127
- * @example
128
- * ```typescript
129
- * // React cleanup
130
- * useEffect(() => {
131
- * const stream = client.sse.markReferences(..., { auth, eventBus });
132
- * return () => stream.close();
133
- * }, []);
134
- * ```
135
- */
136
- close(): void;
6
+ interface ViewModel {
7
+ dispose(): void;
137
8
  }
9
+ declare function createDisposer(): {
10
+ add(vm: ViewModel | (() => void)): void;
11
+ dispose(): void;
12
+ };
138
13
 
139
- /**
140
- * SSE meta event for stream connection lifecycle
141
- * Internal to SSE infrastructure, not part of core event protocol
142
- */
143
- declare const SSE_STREAM_CONNECTED: "stream-connected";
144
- type SSEStreamConnected = typeof SSE_STREAM_CONNECTED;
145
- /**
146
- * SSE Client configuration
147
- */
148
- interface SSEClientConfig {
149
- baseUrl: BaseUrl;
150
- logger?: Logger;
14
+ interface BusEvent {
15
+ channel: string;
16
+ payload: Record<string, unknown>;
17
+ scope?: string;
151
18
  }
152
- /**
153
- * Options for SSE requests
154
- */
155
- interface SSERequestOptions {
156
- auth?: AccessToken;
157
- /** EventBus for event-driven architecture (required) */
158
- eventBus: _semiont_core.EventBus;
19
+ interface ActorVMOptions {
20
+ baseUrl: string;
21
+ token: string | (() => string);
22
+ channels: string[];
23
+ scope?: string;
24
+ reconnectMs?: number;
159
25
  }
160
26
  /**
161
- * SSE Client for long-lived streaming connections.
162
- *
163
- * This client has three methods — one per long-lived broadcast stream.
164
- * Per-operation SSE routes have been replaced by plain HTTP POSTs with
165
- * results delivered via the events-stream.
166
- *
167
- * Uses native fetch() instead of ky for SSE support.
168
- * Auth tokens are passed per-request via options.
169
- *
170
- * @example
171
- * ```typescript
172
- * const sseClient = new SSEClient({ baseUrl: 'http://localhost:4000' });
27
+ * Connection state exposed by the SSE bus actor.
173
28
  *
174
- * // Open a long-lived resource events stream (auto-reconnects on disconnect)
175
- * const stream = sseClient.resourceEvents(resourceId, { auth, eventBus });
29
+ * initial ─ before start() has been called
30
+ * connecting ─ fetch() in flight, no bytes yet
31
+ * open ─ SSE stream live, first byte seen
32
+ * reconnecting ─ open → dropped, retrying; may be transient
33
+ * degraded ─ has been reconnecting for > DEGRADED_THRESHOLD_MS;
34
+ * UI banner threshold; distinguishes brief mount-
35
+ * churn cycles from sustained disconnection
36
+ * closed ─ stop()/dispose() called; terminal
176
37
  *
177
- * // Events auto-route to EventBus typed channels
178
- * eventBus.get('mark:body-updated').subscribe((event) => { ... });
179
- *
180
- * // Close when done
181
- * stream.close();
182
- * ```
38
+ * Transition rules are enforced by a helper that throws on invalid
39
+ * transitions catches bugs in the reconnect loop that would
40
+ * otherwise strand the observable in a lying value.
183
41
  */
184
- declare class SSEClient {
185
- private baseUrl;
186
- private logger?;
187
- constructor(config: SSEClientConfig);
188
- /**
189
- * Get common headers for SSE requests
190
- */
191
- private getHeaders;
192
- /**
193
- * Subscribe to resource events (long-lived stream)
194
- *
195
- * Opens a long-lived SSE connection to receive real-time events for a resource.
196
- * Used for collaborative editing - see events from other users as they happen.
197
- *
198
- * This stream does NOT have a complete event - it stays open until explicitly closed.
199
- *
200
- * @param resourceId - Resource URI or ID to subscribe to
201
- * @param options - Request options (auth token)
202
- * @returns SSE stream controller with event callback
203
- *
204
- * @example
205
- * ```typescript
206
- * const stream = sseClient.resourceEvents(
207
- * 'http://localhost:4000/resources/doc-123',
208
- * { auth: 'your-token' }
209
- * );
210
- *
211
- * stream.onProgress((event) => {
212
- * console.log(`Event: ${event.type}`);
213
- * console.log(`User: ${event.userId}`);
214
- * console.log(`Sequence: ${event.metadata.sequenceNumber}`);
215
- * console.log(`Payload:`, event.payload);
216
- * });
217
- *
218
- * stream.onError((error) => {
219
- * console.error('Stream error:', error.message);
220
- * });
221
- *
222
- * // Close when no longer needed (e.g., component unmount)
223
- * stream.close();
224
- * ```
225
- */
226
- resourceEvents(resourceId: ResourceId, options: SSERequestOptions & {
227
- onConnected?: () => void;
228
- }): SSEStream;
229
- /**
230
- * Subscribe to global system events (long-lived stream)
231
- *
232
- * Opens a long-lived SSE connection to receive system-level domain events
233
- * (entity type additions, etc.) that are not scoped to a specific resource.
234
- *
235
- * @param options - Request options (auth token, eventBus)
236
- * @returns SSE stream controller
237
- *
238
- * @example
239
- * ```typescript
240
- * const stream = sseClient.globalEvents({ auth: 'your-token', eventBus });
241
- *
242
- * // Events auto-emit to EventBus typed channels — subscribe there
243
- * eventBus.get('mark:entity-type-added').subscribe((stored) => {
244
- * // Invalidate entity types query
245
- * });
246
- *
247
- * // Close when no longer needed
248
- * stream.close();
249
- * ```
250
- */
251
- globalEvents(options: SSERequestOptions & {
252
- onConnected?: () => void;
253
- }): SSEStream;
254
- /**
255
- * Subscribe to participant attention stream (long-lived stream)
256
- *
257
- * Opens a participant-scoped SSE connection to receive cross-participant
258
- * beckon signals. Signals are delivered as 'beckon:focus' events routed
259
- * to the EventBus — the existing scroll/highlight machinery handles them.
260
- *
261
- * Signals are ephemeral — delivered if connected, dropped if not.
262
- *
263
- * @param options - Request options (auth token, eventBus)
264
- * @returns SSE stream controller
265
- */
266
- attentionStream(options: SSERequestOptions & {
267
- onConnected?: () => void;
268
- }): SSEStream;
42
+ type ConnectionState = 'initial' | 'connecting' | 'open' | 'reconnecting' | 'degraded' | 'closed';
43
+ /** Time in the `reconnecting` state before transitioning to `degraded`. */
44
+ declare const DEGRADED_THRESHOLD_MS = 3000;
45
+ interface ActorVM extends ViewModel {
46
+ on$<T = Record<string, unknown>>(channel: string): Observable<T>;
47
+ emit(channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<void>;
48
+ state$: Observable<ConnectionState>;
49
+ addChannels(channels: string[], scope?: string): void;
50
+ removeChannels(channels: string[]): void;
51
+ start(): void;
52
+ stop(): void;
269
53
  }
54
+ declare function createActorVM(options: ActorVMOptions): ActorVM;
270
55
 
271
56
  /**
272
57
  * Verb Namespace Interfaces
@@ -280,19 +65,20 @@ declare class SSEClient {
280
65
  * proxy handles HTTP, auth, SSE, and caching internally.
281
66
  *
282
67
  * Return type conventions:
283
- * - Browse live queries → Observable (events-stream driven, cached)
68
+ * - Browse live queries → Observable (bus gateway driven, cached)
284
69
  * - Browse one-shot reads → Promise (fetch once, no cache)
285
70
  * - Commands (mark, bind, yield.resource) → Promise (fire-and-forget)
286
71
  * - Long-running ops (gather, match, yield.fromAnnotation, mark.assist) → Observable (progress + result)
287
72
  * - Ephemeral signals (beckon) → void
288
73
  */
289
74
 
290
- type Annotation$1 = components['schemas']['Annotation'];
291
- type ResourceDescriptor$2 = components['schemas']['ResourceDescriptor'];
292
- type StoredEventResponse$1 = components['schemas']['StoredEventResponse'];
75
+ type Annotation$2 = components['schemas']['Annotation'];
76
+ type ResourceDescriptor$5 = components['schemas']['ResourceDescriptor'];
77
+ type StoredEventResponse$2 = components['schemas']['StoredEventResponse'];
293
78
  type GatherProgress = components['schemas']['GatherProgress'];
294
79
  type MatchSearchResult = components['schemas']['MatchSearchResult'];
295
- type MarkAssistFinished = components['schemas']['MarkAssistFinished'];
80
+ type JobProgress$2 = components['schemas']['JobProgress'];
81
+ type YieldProgress$1 = JobProgress$2;
296
82
  type GatherAnnotationComplete = components['schemas']['GatherAnnotationComplete'];
297
83
  type JobStatusResponse$1 = components['schemas']['JobStatusResponse'];
298
84
  type AuthResponse$1 = components['schemas']['AuthResponse'];
@@ -314,6 +100,14 @@ type ResponseContent$1<T> = T extends {
314
100
  };
315
101
  };
316
102
  };
103
+ } ? R : T extends {
104
+ responses: {
105
+ 202: {
106
+ content: {
107
+ 'application/json': infer R;
108
+ };
109
+ };
110
+ };
317
111
  } ? R : never;
318
112
  type RequestContent$1<T> = T extends {
319
113
  requestBody?: {
@@ -323,7 +117,7 @@ type RequestContent$1<T> = T extends {
323
117
  };
324
118
  } ? R : never;
325
119
  /** Input for creating an annotation via mark.annotation() */
326
- type CreateAnnotationInput = RequestContent$1<paths['/resources/{id}/annotations']['post']>;
120
+ type CreateAnnotationInput = components['schemas']['CreateAnnotationRequest'];
327
121
  /** Input for creating a resource via yield.resource() */
328
122
  interface CreateResourceInput {
329
123
  name: string;
@@ -335,6 +129,11 @@ interface CreateResourceInput {
335
129
  sourceAnnotationId?: string;
336
130
  sourceResourceId?: string;
337
131
  storageUri: string;
132
+ /** Prompt that drove AI generation (for AI-generated resources). */
133
+ generationPrompt?: string;
134
+ /** Agent(s) that generated the content (for AI-generated resources). */
135
+ generator?: components['schemas']['Agent'] | components['schemas']['Agent'][];
136
+ isDraft?: boolean;
338
137
  }
339
138
  /** Options for yield.fromAnnotation() */
340
139
  interface GenerationOptions {
@@ -358,11 +157,16 @@ interface MarkAssistOptions {
358
157
  categories?: string[];
359
158
  }
360
159
  /** Options for yield.createFromToken() */
361
- type CreateFromTokenOptions = RequestContent$1<paths['/api/clone-tokens/create-resource']['post']>;
160
+ type CreateFromTokenOptions = {
161
+ token: string;
162
+ name: string;
163
+ content: string;
164
+ archiveOriginal?: boolean;
165
+ };
362
166
  /** Referenced-by entry from browse.referencedBy() */
363
- type ReferencedByEntry = ResponseContent$1<paths['/resources/{id}/referenced-by']['get']>['referencedBy'][number];
167
+ type ReferencedByEntry = components['schemas']['GetReferencedByResponse']['referencedBy'][number];
364
168
  /** Annotation history from browse.annotationHistory() */
365
- type AnnotationHistoryResponse = ResponseContent$1<paths['/resources/{resourceId}/annotations/{annotationId}/history']['get']>;
169
+ type AnnotationHistoryResponse = components['schemas']['GetAnnotationHistoryResponse'];
366
170
  /** User object from auth/admin responses */
367
171
  type User = AuthResponse$1['user'];
368
172
  /**
@@ -377,30 +181,31 @@ type GatherAnnotationProgress = GatherProgress | GatherAnnotationComplete;
377
181
  type MatchSearchProgress = MatchSearchResult;
378
182
  /**
379
183
  * Progress emitted by mark.assist() Observable.
380
- * Emits MarkProgress during scanning, then MarkAssistFinished on completion.
381
- * On failure, the Observable errors with MarkAssistFailed.
184
+ * Each emission is a JobProgress snapshot (unified job lifecycle). The
185
+ * Observable completes on `job:complete`; errors on `job:fail`.
382
186
  */
383
- type MarkAssistProgress = MarkProgress | MarkAssistFinished;
187
+ type MarkAssistProgress = JobProgress$2;
384
188
  /**
385
189
  * Browse — reads from materialized views
386
190
  *
387
191
  * Live queries return Observables that emit initial state and re-emit
388
- * on events-stream updates. One-shot reads return Promises.
192
+ * on bus gateway updates. One-shot reads return Promises.
389
193
  *
390
194
  * Backend actor: Browser (context classes)
391
195
  * Event prefix: browse:*
392
196
  */
393
197
  interface BrowseNamespace$1 {
394
- resource(resourceId: ResourceId): Observable<ResourceDescriptor$2 | undefined>;
198
+ resource(resourceId: ResourceId): Observable<ResourceDescriptor$5 | undefined>;
395
199
  resources(filters?: {
396
200
  limit?: number;
397
201
  archived?: boolean;
398
202
  search?: string;
399
- }): Observable<ResourceDescriptor$2[] | undefined>;
400
- annotations(resourceId: ResourceId): Observable<Annotation$1[] | undefined>;
401
- annotation(resourceId: ResourceId, annotationId: AnnotationId): Observable<Annotation$1 | undefined>;
203
+ }): Observable<ResourceDescriptor$5[] | undefined>;
204
+ annotations(resourceId: ResourceId): Observable<Annotation$2[] | undefined>;
205
+ annotation(resourceId: ResourceId, annotationId: AnnotationId): Observable<Annotation$2 | undefined>;
402
206
  entityTypes(): Observable<string[] | undefined>;
403
207
  referencedBy(resourceId: ResourceId): Observable<ReferencedByEntry[] | undefined>;
208
+ events(resourceId: ResourceId): Observable<StoredEventResponse$2[] | undefined>;
404
209
  resourceContent(resourceId: ResourceId): Promise<string>;
405
210
  resourceRepresentation(resourceId: ResourceId, options?: {
406
211
  accept?: string;
@@ -414,18 +219,18 @@ interface BrowseNamespace$1 {
414
219
  stream: ReadableStream<Uint8Array>;
415
220
  contentType: string;
416
221
  }>;
417
- resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse$1[]>;
222
+ resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse$2[]>;
418
223
  annotationHistory(resourceId: ResourceId, annotationId: AnnotationId): Promise<AnnotationHistoryResponse>;
419
224
  connections(resourceId: ResourceId): Promise<GraphConnection[]>;
420
- backlinks(resourceId: ResourceId): Promise<Annotation$1[]>;
421
- resourcesByName(query: string, limit?: number): Promise<ResourceDescriptor$2[]>;
422
- files(dirPath?: string, sort?: 'name' | 'mtime' | 'annotationCount'): Promise<ResponseContent$1<paths['/api/browse/files']['get']>>;
225
+ backlinks(resourceId: ResourceId): Promise<Annotation$2[]>;
226
+ resourcesByName(query: string, limit?: number): Promise<ResourceDescriptor$5[]>;
227
+ files(dirPath?: string, sort?: 'name' | 'mtime' | 'annotationCount'): Promise<components['schemas']['BrowseFilesResponse']>;
423
228
  }
424
229
  /**
425
230
  * Mark — annotation CRUD, entity types, AI assist
426
231
  *
427
232
  * Commands return Promises that resolve on HTTP acceptance (202).
428
- * Results appear on browse Observables via events-stream.
233
+ * Results appear on browse Observables via bus gateway.
429
234
  * assist() returns an Observable for long-running progress.
430
235
  *
431
236
  * Backend actor: Stower
@@ -506,7 +311,7 @@ interface YieldNamespace$1 {
506
311
  token: string;
507
312
  expiresAt: string;
508
313
  }>;
509
- fromToken(token: string): Promise<ResourceDescriptor$2>;
314
+ fromToken(token: string): Promise<ResourceDescriptor$5>;
510
315
  createFromToken(options: CreateFromTokenOptions): Promise<{
511
316
  resourceId: string;
512
317
  }>;
@@ -586,53 +391,55 @@ interface AdminNamespace$1 {
586
391
  }>;
587
392
  }
588
393
 
589
- /**
590
- * BrowseNamespace — reads from materialized views
591
- *
592
- * Absorbs AnnotationStore and ResourceStore logic. Live queries return
593
- * Observables backed by BehaviorSubjects that update reactively when
594
- * EventBus events arrive. One-shot reads are Promise wrappers over HTTP.
595
- *
596
- * Backend actor: Browser (context classes)
597
- * Event prefix: browse:*
598
- */
599
-
600
- type Annotation = components['schemas']['Annotation'];
601
- type ResourceDescriptor$1 = components['schemas']['ResourceDescriptor'];
602
- type StoredEventResponse = components['schemas']['StoredEventResponse'];
603
- type TokenGetter$9 = () => AccessToken | undefined;
394
+ type Annotation$1 = components['schemas']['Annotation'];
395
+ type ResourceDescriptor$4 = components['schemas']['ResourceDescriptor'];
396
+ type StoredEventResponse$1 = components['schemas']['StoredEventResponse'];
397
+ type TokenGetter$4 = () => AccessToken | undefined;
398
+ type ResourceListFilters = {
399
+ limit?: number;
400
+ archived?: boolean;
401
+ search?: string;
402
+ };
604
403
  declare class BrowseNamespace implements BrowseNamespace$1 {
605
404
  private readonly http;
606
405
  private readonly eventBus;
607
- private readonly annotationList$;
608
- private readonly fetchingAnnotationList;
609
- private readonly annotationListObs$;
610
- private readonly annotationDetail$;
611
- private readonly fetchingAnnotationDetail;
612
- private readonly annotationDetailObs$;
613
- private readonly resourceDetail$;
614
- private readonly fetchingResourceDetail;
615
- private readonly resourceDetailObs$;
616
- private readonly resourceList$;
617
- private readonly fetchingResourceList;
618
- private readonly resourceListObs$;
619
- private readonly entityTypes$;
620
- private fetchingEntityTypes;
621
- private readonly referencedBy$;
622
- private readonly fetchingReferencedBy;
623
- private readonly referencedByObs$;
406
+ private readonly resourceCache;
407
+ private readonly resourceListCache;
408
+ private readonly annotationListCache;
409
+ /**
410
+ * Annotation-detail cache keyed by `annotationId` only — the resourceId
411
+ * is a routing hint for the backend fetch, not an identity component.
412
+ * We track the most recent resourceId per annotationId in a side-map
413
+ * so `mark:delete-ok` (which carries only `annotationId`) can reach
414
+ * the right cache entry. Aligns with the pre-refactor semantics.
415
+ */
416
+ private readonly annotationDetailCache;
417
+ private readonly annotationResources;
418
+ private readonly entityTypesCache;
419
+ private readonly referencedByCache;
420
+ private readonly resourceEventsCache;
421
+ /** Filter-blob memory so `invalidateResourceLists` can replay per-key. */
422
+ private readonly resourceListFilters;
423
+ /**
424
+ * Per-key memo for `annotations()` observables. The cache stores the
425
+ * full `AnnotationsListResponse`; the public shape is just the inner
426
+ * `Annotation[]`. Without this memo, every call to `annotations(rId)`
427
+ * would produce a fresh `.pipe(map(...))` observable, violating B4
428
+ * (per-key observable stability). Consumers that compare observable
429
+ * identity — React hooks depending on the observable reference,
430
+ * `distinctUntilChanged` at a higher level — would misbehave.
431
+ */
432
+ private readonly annotationListObs;
624
433
  private readonly getToken;
625
- constructor(http: SemiontApiClient, eventBus: EventBus, getToken: TokenGetter$9);
626
- resource(resourceId: ResourceId): Observable<ResourceDescriptor$1 | undefined>;
627
- resources(filters?: {
628
- limit?: number;
629
- archived?: boolean;
630
- search?: string;
631
- }): Observable<ResourceDescriptor$1[] | undefined>;
632
- annotations(resourceId: ResourceId): Observable<Annotation[] | undefined>;
633
- annotation(resourceId: ResourceId, annotationId: AnnotationId): Observable<Annotation | undefined>;
434
+ private readonly actor;
435
+ constructor(http: SemiontApiClient, eventBus: EventBus, getToken: TokenGetter$4, actor: ActorVM);
436
+ resource(resourceId: ResourceId): Observable<ResourceDescriptor$4 | undefined>;
437
+ resources(filters?: ResourceListFilters): Observable<ResourceDescriptor$4[] | undefined>;
438
+ annotations(resourceId: ResourceId): Observable<Annotation$1[] | undefined>;
439
+ annotation(resourceId: ResourceId, annotationId: AnnotationId): Observable<Annotation$1 | undefined>;
634
440
  entityTypes(): Observable<string[] | undefined>;
635
441
  referencedBy(resourceId: ResourceId): Observable<ReferencedByEntry[] | undefined>;
442
+ events(resourceId: ResourceId): Observable<StoredEventResponse$1[] | undefined>;
636
443
  resourceContent(resourceId: ResourceId): Promise<string>;
637
444
  resourceRepresentation(resourceId: ResourceId, options?: {
638
445
  accept?: string;
@@ -646,45 +453,56 @@ declare class BrowseNamespace implements BrowseNamespace$1 {
646
453
  stream: ReadableStream<Uint8Array>;
647
454
  contentType: string;
648
455
  }>;
649
- resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse[]>;
456
+ resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse$1[]>;
650
457
  annotationHistory(resourceId: ResourceId, annotationId: AnnotationId): Promise<AnnotationHistoryResponse>;
651
458
  connections(_resourceId: ResourceId): Promise<GraphConnection[]>;
652
- backlinks(_resourceId: ResourceId): Promise<Annotation[]>;
653
- resourcesByName(_query: string, _limit?: number): Promise<ResourceDescriptor$1[]>;
654
- files(dirPath?: string, sort?: 'name' | 'mtime' | 'annotationCount'): Promise<ResponseContent$1<paths['/api/browse/files']['get']>>;
459
+ backlinks(_resourceId: ResourceId): Promise<Annotation$1[]>;
460
+ resourcesByName(_query: string, _limit?: number): Promise<ResourceDescriptor$4[]>;
461
+ files(dirPath?: string, sort?: 'name' | 'mtime' | 'annotationCount'): Promise<components['schemas']['BrowseFilesResponse']>;
655
462
  invalidateAnnotationList(resourceId: ResourceId): void;
656
- invalidateAnnotationDetail(annotationId: AnnotationId): void;
463
+ removeAnnotationDetail(annotationId: AnnotationId): void;
657
464
  invalidateResourceDetail(id: ResourceId): void;
658
465
  invalidateResourceLists(): void;
659
466
  invalidateEntityTypes(): void;
660
467
  invalidateReferencedBy(resourceId: ResourceId): void;
661
- updateAnnotationInPlace(resourceId: ResourceId, annotation: Annotation): void;
468
+ invalidateResourceEvents(resourceId: ResourceId): void;
469
+ updateAnnotationInPlace(resourceId: ResourceId, annotation: Annotation$1): void;
470
+ /**
471
+ * Typed shorthand for `eventBus.get(channel).subscribe(handler)`.
472
+ * Preserves per-channel payload typing so handlers read
473
+ * `EventMap[K]` without any casts.
474
+ */
475
+ private on;
476
+ /**
477
+ * Handler shared by `mark:entity-tag-added` and `mark:entity-tag-removed`.
478
+ * Both events carry the same effect: the annotation list, the
479
+ * resource descriptor, and the event log for that resource all may
480
+ * now reflect different entity tagging, so invalidate all three.
481
+ */
482
+ private onEntityTagChanged;
483
+ /**
484
+ * Handler shared by `mark:archived` and `mark:unarchived`. Both
485
+ * change a resource's archived flag, which is stored on the resource
486
+ * descriptor and affects the resource-list filter.
487
+ */
488
+ private onArchiveToggled;
489
+ /**
490
+ * Handler shared by `yield:create-ok` and `yield:update-ok`. Both
491
+ * report a resource mutation with the resourceId as a string (not
492
+ * yet branded), so we brand and apply the same effect as
493
+ * `onArchiveToggled`.
494
+ */
495
+ private onYieldResourceMutated;
662
496
  private subscribeToEvents;
663
- private fetchAnnotationList;
664
- private fetchAnnotationDetail;
665
- private fetchResourceDetail;
666
- private fetchResourceList;
667
- private fetchEntityTypes;
668
- private fetchReferencedBy;
669
497
  }
670
498
 
671
- /**
672
- * MarkNamespace — annotation CRUD, entity types, AI assist
673
- *
674
- * Commands return Promises that resolve on HTTP acceptance.
675
- * Results appear on browse Observables via events-stream.
676
- * assist() returns an Observable for long-running progress.
677
- *
678
- * Backend actor: Stower
679
- * Event prefix: mark:*
680
- */
681
-
682
- type TokenGetter$8 = () => AccessToken | undefined;
499
+ type TokenGetter$3 = () => AccessToken | undefined;
683
500
  declare class MarkNamespace implements MarkNamespace$1 {
684
501
  private readonly http;
685
502
  private readonly eventBus;
686
503
  private readonly getToken;
687
- constructor(http: SemiontApiClient, eventBus: EventBus, getToken: TokenGetter$8);
504
+ private readonly actor;
505
+ constructor(http: SemiontApiClient, eventBus: EventBus, getToken: TokenGetter$3, actor: ActorVM);
688
506
  annotation(resourceId: ResourceId, input: CreateAnnotationInput): Promise<{
689
507
  annotationId: string;
690
508
  }>;
@@ -698,41 +516,16 @@ declare class MarkNamespace implements MarkNamespace$1 {
698
516
  private dispatchAssist;
699
517
  }
700
518
 
701
- /**
702
- * BindNamespace — reference linking
703
- *
704
- * The simplest namespace. One method. The result (updated annotation
705
- * with resolved reference) arrives on browse.annotations() via the
706
- * enriched mark:body-updated event.
707
- *
708
- * Backend actor: Stower (via mark:update-body)
709
- * Event prefix: mark:body-updated (shares mark event pipeline)
710
- */
711
-
712
- type TokenGetter$7 = () => AccessToken | undefined;
713
519
  declare class BindNamespace implements BindNamespace$1 {
714
- private readonly http;
715
- private readonly getToken;
716
- constructor(http: SemiontApiClient, getToken: TokenGetter$7);
520
+ private readonly actor;
521
+ constructor(actor: ActorVM);
717
522
  body(resourceId: ResourceId, annotationId: AnnotationId, operations: BodyOperation[]): Promise<void>;
718
523
  }
719
524
 
720
- /**
721
- * GatherNamespace — context assembly
722
- *
723
- * Long-running (LLM calls + graph traversal). Returns Observables
724
- * that emit progress then the gathered context.
725
- *
726
- * Backend actor: Gatherer
727
- * Event prefix: gather:*
728
- */
729
-
730
- type TokenGetter$6 = () => AccessToken | undefined;
731
525
  declare class GatherNamespace implements GatherNamespace$1 {
732
- private readonly http;
733
526
  private readonly eventBus;
734
- private readonly getToken;
735
- constructor(http: SemiontApiClient, eventBus: EventBus, getToken: TokenGetter$6);
527
+ private readonly actor;
528
+ constructor(eventBus: EventBus, actor: ActorVM);
736
529
  annotation(annotationId: AnnotationId, resourceId: ResourceId, options?: {
737
530
  contextWindow?: number;
738
531
  }): Observable<GatherAnnotationProgress>;
@@ -741,87 +534,50 @@ declare class GatherNamespace implements GatherNamespace$1 {
741
534
  }): Observable<GatherAnnotationProgress>;
742
535
  }
743
536
 
744
- /**
745
- * MatchNamespace — search and ranking
746
- *
747
- * Long-running (semantic search, optional LLM scoring). Returns
748
- * Observable with results.
749
- *
750
- * Backend actor: Matcher
751
- * Event prefix: match:*
752
- */
753
-
754
- type TokenGetter$5 = () => AccessToken | undefined;
755
537
  declare class MatchNamespace implements MatchNamespace$1 {
756
- private readonly http;
757
538
  private readonly eventBus;
758
- private readonly getToken;
759
- constructor(http: SemiontApiClient, eventBus: EventBus, getToken: TokenGetter$5);
539
+ private readonly actor;
540
+ constructor(eventBus: EventBus, actor: ActorVM);
760
541
  search(resourceId: ResourceId, referenceId: string, context: GatheredContext, options?: {
761
542
  limit?: number;
762
543
  useSemanticScoring?: boolean;
763
544
  }): Observable<MatchSearchProgress>;
764
545
  }
765
546
 
766
- /**
767
- * YieldNamespace — resource creation
768
- *
769
- * resource() is synchronous file upload (Promise).
770
- * fromAnnotation() is long-running LLM generation (Observable).
771
- *
772
- * Backend actor: Stower + generation worker
773
- * Event prefix: yield:*
774
- */
547
+ type YieldProgress = components['schemas']['JobProgress'];
775
548
 
776
- type ResourceDescriptor = components['schemas']['ResourceDescriptor'];
777
- type TokenGetter$4 = () => AccessToken | undefined;
549
+ type ResourceDescriptor$3 = components['schemas']['ResourceDescriptor'];
550
+ type TokenGetter$2 = () => AccessToken | undefined;
778
551
  declare class YieldNamespace implements YieldNamespace$1 {
779
552
  private readonly http;
780
553
  private readonly eventBus;
781
554
  private readonly getToken;
782
- constructor(http: SemiontApiClient, eventBus: EventBus, getToken: TokenGetter$4);
555
+ private readonly actor;
556
+ constructor(http: SemiontApiClient, eventBus: EventBus, getToken: TokenGetter$2, actor: ActorVM);
783
557
  resource(data: CreateResourceInput): Promise<{
784
558
  resourceId: string;
785
559
  }>;
786
- fromAnnotation(resourceId: ResourceId, annotationId: AnnotationId, options: GenerationOptions): Observable<YieldProgress$1>;
560
+ fromAnnotation(resourceId: ResourceId, annotationId: AnnotationId, options: GenerationOptions): Observable<YieldProgress>;
787
561
  cloneToken(resourceId: ResourceId): Promise<{
788
562
  token: string;
789
563
  expiresAt: string;
790
564
  }>;
791
- fromToken(token: string): Promise<ResourceDescriptor>;
565
+ fromToken(token: string): Promise<ResourceDescriptor$3>;
792
566
  createFromToken(options: CreateFromTokenOptions): Promise<{
793
567
  resourceId: string;
794
568
  }>;
795
569
  }
796
570
 
797
- /**
798
- * BeckonNamespace — attention coordination
799
- *
800
- * Fire-and-forget. Ephemeral presence signal delivered via the
801
- * attention-stream to other participants.
802
- *
803
- * Backend actor: (frontend relay via attention-stream)
804
- * Event prefix: beckon:*
805
- */
806
-
807
- type TokenGetter$3 = () => AccessToken | undefined;
808
571
  declare class BeckonNamespace implements BeckonNamespace$1 {
809
- private readonly http;
810
- private readonly getToken;
811
- constructor(http: SemiontApiClient, getToken: TokenGetter$3);
572
+ private readonly actor;
573
+ constructor(actor: ActorVM);
812
574
  attention(annotationId: AnnotationId, resourceId: ResourceId): void;
813
575
  }
814
576
 
815
- /**
816
- * JobNamespace — worker lifecycle
817
- */
818
-
819
577
  type JobStatusResponse = components['schemas']['JobStatusResponse'];
820
- type TokenGetter$2 = () => AccessToken | undefined;
821
578
  declare class JobNamespace implements JobNamespace$1 {
822
- private readonly http;
823
- private readonly getToken;
824
- constructor(http: SemiontApiClient, getToken: TokenGetter$2);
579
+ private readonly actor;
580
+ constructor(actor: ActorVM);
825
581
  status(jobId: JobId): Promise<JobStatusResponse>;
826
582
  pollUntilComplete(jobId: JobId, options?: {
827
583
  interval?: number;
@@ -923,6 +679,14 @@ type ResponseContent<T> = T extends {
923
679
  };
924
680
  };
925
681
  };
682
+ } ? R : T extends {
683
+ responses: {
684
+ 202: {
685
+ content: {
686
+ 'application/json': infer R;
687
+ };
688
+ };
689
+ };
926
690
  } ? R : never;
927
691
  type RequestContent<T> = T extends {
928
692
  requestBody?: {
@@ -944,27 +708,24 @@ declare class APIError extends Error {
944
708
  * 401 propagates as an APIError.
945
709
  *
946
710
  * Implementations must dedupe concurrent calls so that simultaneous 401s
947
- * don't fire multiple parallel refresh requests. The frontend's
948
- * KnowledgeBaseSessionProvider provides this via an in-flight Promise map
949
- * keyed by KB id.
711
+ * don't fire multiple parallel refresh requests. `SemiontSession.refresh()`
712
+ * satisfies this via an in-flight Promise map keyed by KB id.
950
713
  */
951
714
  type TokenRefresher = () => Promise<string | null>;
952
715
  interface SemiontApiClientConfig {
953
716
  baseUrl: BaseUrl;
954
- /** Per-workspace EventBus. Required — one bus per workspace, constructed externally. */
955
- eventBus: EventBus;
717
+ /**
718
+ * Observable access token. All auth (HTTP + bus) reads the current value.
719
+ * Update by calling `.next(newToken)` on the BehaviorSubject. The bus actor
720
+ * auto-starts when the value transitions from null to a real token.
721
+ * If omitted, the client operates unauthenticated (public endpoints only).
722
+ */
723
+ token$?: BehaviorSubject<AccessToken | null>;
956
724
  timeout?: number;
957
725
  retry?: number;
958
726
  logger?: Logger;
959
727
  /** Optional 401-recovery hook. See {@link TokenRefresher}. */
960
728
  tokenRefresher?: TokenRefresher;
961
- /**
962
- * Token getter for the verb-namespace API (client.browse, client.mark, etc.).
963
- * When provided, auth is managed internally — no per-call auth needed.
964
- * The getter is called on each request to get the current token.
965
- * If not provided, namespace methods use undefined auth (public endpoints only).
966
- */
967
- getToken?: () => AccessToken | undefined;
968
729
  }
969
730
  /**
970
731
  * Options for individual API requests
@@ -982,21 +743,18 @@ interface RequestOptions {
982
743
  declare class SemiontApiClient {
983
744
  private http;
984
745
  readonly baseUrl: BaseUrl;
985
- /** The workspace-scoped EventBus this client was constructed with. */
986
- readonly eventBus: EventBus;
987
- private logger?;
988
746
  /**
989
- * Shared mutable token getter. All verb namespaces read from this.
990
- * Updated via setTokenGetter() from the React auth layer.
747
+ * Workspace-scoped EventBus owned by the client, constructed
748
+ * internally, never accepted from config. Private: all bus access
749
+ * goes through `client.emit` / `client.on` / `client.stream`.
991
750
  */
992
- private _getToken;
751
+ private readonly eventBus;
752
+ private logger?;
993
753
  /**
994
- * SSE streaming client for real-time operations
995
- *
996
- * Separate from the main HTTP client to clearly mark streaming endpoints.
997
- * Uses native fetch() instead of ky for SSE support.
754
+ * Observable token source. All auth reads from this.
998
755
  */
999
- readonly sse: SSEClient;
756
+ private readonly token$;
757
+ private _actor;
1000
758
  readonly browse: BrowseNamespace;
1001
759
  readonly mark: MarkNamespace;
1002
760
  readonly bind: BindNamespace;
@@ -1008,12 +766,41 @@ declare class SemiontApiClient {
1008
766
  readonly auth: AuthNamespace;
1009
767
  readonly admin: AdminNamespace;
1010
768
  constructor(config: SemiontApiClientConfig);
769
+ private _actorStarted;
770
+ get actor(): ActorVM;
771
+ private activeResource;
1011
772
  /**
1012
- * Update the token getter for all verb namespaces.
1013
- * Called from the React auth layer when the token changes.
1014
- * All namespaces share this getter via closure — no per-namespace sync needed.
773
+ * Subscribe the bus actor to the resource-scoped SSE stream for a single
774
+ * resource and bridge incoming scoped events into the workspace event bus.
775
+ *
776
+ * **One distinct scope at a time**: the client supports subscriptions
777
+ * to a single resource scope concurrently. Multiple calls with the
778
+ * **same** resourceId are ref-counted — each returns an independent
779
+ * unsubscribe; the underlying SSE scope is torn down only when the
780
+ * last unsubscribe fires. Calling with a **different** resourceId
781
+ * while a subscription is live throws. Widening this to multiple
782
+ * concurrent scopes is deferred until a product requirement (e.g.
783
+ * split-pane viewer, headless fleet-watcher) forces the design.
784
+ *
785
+ * @returns a disposer that decrements the ref count (and tears down
786
+ * the SSE scope + bridges when it reaches zero).
787
+ */
788
+ subscribeToResource(resourceId: ResourceId): () => void;
789
+ private makeUnsubscriber;
790
+ dispose(): void;
791
+ /** Emit an event on the internal bus. */
792
+ emit<K extends keyof EventMap>(channel: K, payload: EventMap[K]): void;
793
+ /** Subscribe to an event on the internal bus; returns unsubscribe. */
794
+ on<K extends keyof EventMap>(channel: K, handler: (payload: EventMap[K]) => void): () => void;
795
+ /** Read-only observable for a bus channel. Consumers `.pipe(...)` over this. */
796
+ stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]>;
797
+ /**
798
+ * Build the `Authorization: Bearer <token>` header. If the caller passed
799
+ * an explicit `{ auth }` it wins (used by session-internal throwaway
800
+ * clients that need to run a validation request with a specific token).
801
+ * Otherwise the current value of `this.token$` is used, so external
802
+ * callers never have to plumb the token themselves.
1015
803
  */
1016
- setTokenGetter(getter: () => AccessToken | undefined): void;
1017
804
  private authHeaders;
1018
805
  authenticatePassword(email: Email, password: string, options?: RequestOptions): Promise<ResponseContent<paths['/api/tokens/password']['post']>>;
1019
806
  refreshToken(token: RefreshToken, options?: RequestOptions): Promise<ResponseContent<paths['/api/tokens/refresh']['post']>>;
@@ -1037,6 +824,8 @@ declare class SemiontApiClient {
1037
824
  * @param data.creationMethod - Optional creation method
1038
825
  * @param data.sourceAnnotationId - Optional source annotation ID
1039
826
  * @param data.sourceResourceId - Optional source resource ID
827
+ * @param data.generationPrompt - Optional prompt that drove AI generation
828
+ * @param data.generator - Optional Agent(s) that generated the content
1040
829
  * @param options - Request options including auth
1041
830
  */
1042
831
  yieldResource(data: {
@@ -1049,10 +838,11 @@ declare class SemiontApiClient {
1049
838
  sourceAnnotationId?: string;
1050
839
  sourceResourceId?: string;
1051
840
  storageUri: string;
1052
- }, options?: RequestOptions): Promise<{
1053
- resourceId: string;
1054
- }>;
1055
- browseResource(id: ResourceId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{id}']['get']>>;
841
+ generationPrompt?: string;
842
+ generator?: components['schemas']['Agent'] | components['schemas']['Agent'][];
843
+ isDraft?: boolean;
844
+ }, options?: RequestOptions): Promise<ResponseContent<paths['/resources']['post']>>;
845
+ browseResource(id: ResourceId, _options?: RequestOptions): Promise<components['schemas']['GetResourceResponse']>;
1056
846
  /**
1057
847
  * Get resource representation using W3C content negotiation
1058
848
  * Returns raw binary content (images, PDFs, text, etc.) with content type
@@ -1125,39 +915,47 @@ declare class SemiontApiClient {
1125
915
  stream: ReadableStream<Uint8Array>;
1126
916
  contentType: string;
1127
917
  }>;
1128
- browseResources(limit?: number, archived?: boolean, query?: SearchQuery, options?: RequestOptions): Promise<ResponseContent<paths['/resources']['get']>>;
1129
- updateResource(id: ResourceId, data: RequestContent<paths['/resources/{id}']['patch']>, options?: RequestOptions): Promise<void>;
1130
- getResourceEvents(id: ResourceId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{id}/events']['get']>>;
1131
- browseReferences(id: ResourceId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{id}/referenced-by']['get']>>;
1132
- generateCloneToken(id: ResourceId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{id}/clone-with-token']['post']>>;
1133
- getResourceByToken(token: CloneToken, options?: RequestOptions): Promise<ResponseContent<paths['/api/clone-tokens/{token}']['get']>>;
1134
- createResourceFromToken(data: RequestContent<paths['/api/clone-tokens/create-resource']['post']>, options?: RequestOptions): Promise<{
918
+ browseResources(limit?: number, archived?: boolean, query?: SearchQuery, _options?: RequestOptions): Promise<components['schemas']['ListResourcesResponse']>;
919
+ updateResource(id: ResourceId, data: {
920
+ archived?: boolean;
921
+ entityTypes?: string[];
922
+ }, options?: RequestOptions): Promise<void>;
923
+ getResourceEvents(id: ResourceId, _options?: RequestOptions): Promise<components['schemas']['GetEventsResponse']>;
924
+ browseReferences(id: ResourceId, _options?: RequestOptions): Promise<components['schemas']['GetReferencedByResponse']>;
925
+ generateCloneToken(id: ResourceId, _options?: RequestOptions): Promise<components['schemas']['CloneResourceWithTokenResponse']>;
926
+ getResourceByToken(token: CloneToken, _options?: RequestOptions): Promise<components['schemas']['GetResourceByTokenResponse']>;
927
+ createResourceFromToken(data: {
928
+ token: string;
929
+ name: string;
930
+ content: string;
931
+ archiveOriginal?: boolean;
932
+ }, _options?: RequestOptions): Promise<{
1135
933
  resourceId: string;
1136
934
  }>;
1137
- markAnnotation(id: ResourceId, data: RequestContent<paths['/resources/{id}/annotations']['post']>, options?: RequestOptions): Promise<{
935
+ markAnnotation(id: ResourceId, data: components['schemas']['CreateAnnotationRequest'], _options?: RequestOptions): Promise<{
1138
936
  annotationId: string;
1139
937
  }>;
1140
- getAnnotation(id: AnnotationId, options?: RequestOptions): Promise<ResponseContent<paths['/annotations/{id}']['get']>>;
1141
- browseAnnotation(resourceId: ResourceId, annotationId: AnnotationId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{resourceId}/annotations/{annotationId}']['get']>>;
1142
- browseAnnotations(id: ResourceId, motivation?: Motivation, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{id}/annotations']['get']>>;
1143
- deleteAnnotation(resourceId: ResourceId, annotationId: AnnotationId, options?: RequestOptions): Promise<void>;
938
+ getAnnotation(id: AnnotationId, _options?: RequestOptions): Promise<components['schemas']['GetAnnotationResponse']>;
939
+ browseAnnotation(resourceId: ResourceId, annotationId: AnnotationId, _options?: RequestOptions): Promise<components['schemas']['GetAnnotationResponse']>;
940
+ browseAnnotations(id: ResourceId, _motivation?: Motivation, _options?: RequestOptions): Promise<components['schemas']['GetAnnotationsResponse']>;
941
+ deleteAnnotation(resourceId: ResourceId, annotationId: AnnotationId, _options?: RequestOptions): Promise<void>;
1144
942
  bindAnnotation(resourceId: ResourceId, annotationId: AnnotationId, data: {
1145
943
  operations: BodyOperation[];
1146
- }, options?: RequestOptions): Promise<{
944
+ }, _options?: RequestOptions): Promise<{
1147
945
  correlationId: string;
1148
946
  }>;
1149
- getAnnotationHistory(resourceId: ResourceId, annotationId: AnnotationId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{resourceId}/annotations/{annotationId}/history']['get']>>;
947
+ getAnnotationHistory(resourceId: ResourceId, annotationId: AnnotationId, _options?: RequestOptions): Promise<components['schemas']['GetAnnotationHistoryResponse']>;
1150
948
  annotateReferences(resourceId: ResourceId, data: {
1151
949
  entityTypes: string[];
1152
950
  includeDescriptiveReferences?: boolean;
1153
- }, options?: RequestOptions): Promise<{
951
+ }, _options?: RequestOptions): Promise<{
1154
952
  correlationId: string;
1155
953
  jobId: string;
1156
954
  }>;
1157
955
  annotateHighlights(resourceId: ResourceId, data: {
1158
956
  instructions?: string;
1159
957
  density?: number;
1160
- }, options?: RequestOptions): Promise<{
958
+ }, _options?: RequestOptions): Promise<{
1161
959
  correlationId: string;
1162
960
  jobId: string;
1163
961
  }>;
@@ -1166,7 +964,7 @@ declare class SemiontApiClient {
1166
964
  tone?: string;
1167
965
  density?: number;
1168
966
  language?: string;
1169
- }, options?: RequestOptions): Promise<{
967
+ }, _options?: RequestOptions): Promise<{
1170
968
  correlationId: string;
1171
969
  jobId: string;
1172
970
  }>;
@@ -1175,14 +973,14 @@ declare class SemiontApiClient {
1175
973
  tone?: string;
1176
974
  density?: number;
1177
975
  language?: string;
1178
- }, options?: RequestOptions): Promise<{
976
+ }, _options?: RequestOptions): Promise<{
1179
977
  correlationId: string;
1180
978
  jobId: string;
1181
979
  }>;
1182
980
  annotateTags(resourceId: ResourceId, data: {
1183
981
  schemaId: string;
1184
982
  categories: string[];
1185
- }, options?: RequestOptions): Promise<{
983
+ }, _options?: RequestOptions): Promise<{
1186
984
  correlationId: string;
1187
985
  jobId: string;
1188
986
  }>;
@@ -1194,14 +992,14 @@ declare class SemiontApiClient {
1194
992
  language?: string;
1195
993
  temperature?: number;
1196
994
  maxTokens?: number;
1197
- }, options?: RequestOptions): Promise<{
995
+ }, _options?: RequestOptions): Promise<{
1198
996
  correlationId: string;
1199
997
  jobId: string;
1200
998
  }>;
1201
999
  gatherAnnotationContext(resourceId: ResourceId, annotationId: AnnotationId, data: {
1202
1000
  correlationId: string;
1203
1001
  contextWindow?: number;
1204
- }, options?: RequestOptions): Promise<{
1002
+ }, _options?: RequestOptions): Promise<{
1205
1003
  correlationId: string;
1206
1004
  }>;
1207
1005
  matchSearch(resourceId: ResourceId, data: {
@@ -1210,13 +1008,17 @@ declare class SemiontApiClient {
1210
1008
  context: unknown;
1211
1009
  limit?: number;
1212
1010
  useSemanticScoring?: boolean;
1213
- }, options?: RequestOptions): Promise<{
1011
+ }, _options?: RequestOptions): Promise<{
1214
1012
  correlationId: string;
1215
1013
  }>;
1216
- addEntityType(type: EntityType, options?: RequestOptions): Promise<void>;
1217
- addEntityTypesBulk(types: EntityType[], options?: RequestOptions): Promise<void>;
1218
- listEntityTypes(options?: RequestOptions): Promise<ResponseContent<paths['/api/entity-types']['get']>>;
1219
- beckonAttention(participantId: string, data: RequestContent<paths['/api/participants/{id}/attention']['post']>, options?: RequestOptions): Promise<ResponseContent<paths['/api/participants/{id}/attention']['post']>>;
1014
+ addEntityType(type: EntityType, _options?: RequestOptions): Promise<void>;
1015
+ addEntityTypesBulk(types: EntityType[], _options?: RequestOptions): Promise<void>;
1016
+ listEntityTypes(_options?: RequestOptions): Promise<components['schemas']['GetEntityTypesResponse']>;
1017
+ beckonAttention(_participantId: string, data: {
1018
+ annotationId?: string;
1019
+ resourceId: string;
1020
+ message?: string;
1021
+ }, _options?: RequestOptions): Promise<components['schemas']['BeckonResponse']>;
1220
1022
  listUsers(options?: RequestOptions): Promise<ResponseContent<paths['/api/admin/users']['get']>>;
1221
1023
  getUserStats(options?: RequestOptions): Promise<ResponseContent<paths['/api/admin/users/stats']['get']>>;
1222
1024
  /**
@@ -1268,7 +1070,7 @@ declare class SemiontApiClient {
1268
1070
  result?: Record<string, unknown>;
1269
1071
  }>;
1270
1072
  private parseSSEStream;
1271
- getJobStatus(id: JobId, options?: RequestOptions): Promise<ResponseContent<paths['/api/jobs/{id}']['get']>>;
1073
+ getJobStatus(id: JobId, _options?: RequestOptions): Promise<components['schemas']['JobStatusResponse']>;
1272
1074
  /**
1273
1075
  * Poll a job until it completes or fails
1274
1076
  * @param id - The job ID to poll
@@ -1278,13 +1080,935 @@ declare class SemiontApiClient {
1278
1080
  pollJobUntilComplete(id: JobId, options?: {
1279
1081
  interval?: number;
1280
1082
  timeout?: number;
1281
- onProgress?: (status: ResponseContent<paths['/api/jobs/{id}']['get']>) => void;
1083
+ onProgress?: (status: components['schemas']['JobStatusResponse']) => void;
1282
1084
  auth?: AccessToken;
1283
- }): Promise<ResponseContent<paths['/api/jobs/{id}']['get']>>;
1085
+ }): Promise<components['schemas']['JobStatusResponse']>;
1284
1086
  healthCheck(options?: RequestOptions): Promise<ResponseContent<paths['/api/health']['get']>>;
1285
1087
  getStatus(options?: RequestOptions): Promise<ResponseContent<paths['/api/status']['get']>>;
1286
- browseFiles(dirPath?: string, sort?: 'name' | 'mtime' | 'annotationCount', options?: RequestOptions): Promise<ResponseContent<paths['/api/browse/files']['get']>>;
1088
+ browseFiles(dirPath?: string, sort?: 'name' | 'mtime' | 'annotationCount', _options?: RequestOptions): Promise<components['schemas']['BrowseFilesResponse']>;
1089
+ }
1090
+
1091
+ declare class BusRequestError extends Error {
1092
+ constructor(message: string);
1093
+ }
1094
+ declare function busRequest<TResult>(actor: ActorVM, emitChannel: string, payload: Record<string, unknown>, resultChannel: string, failureChannel: string, timeoutMs?: number): Promise<TResult>;
1095
+
1096
+ /**
1097
+ * RxJS-native read-through cache primitive.
1098
+ *
1099
+ * Behavioral contract: packages/api-client/docs/CACHE-SEMANTICS.md (B1–B13).
1100
+ *
1101
+ * Framework-agnostic: no React, no dependency on any namespace. Used by
1102
+ * `BrowseNamespace` to back its per-key stores, but equally usable from
1103
+ * CLI, MCP, or worker code.
1104
+ *
1105
+ * Shape:
1106
+ * - `observe(key)`: returns an Observable<V | undefined> that triggers
1107
+ * a fetch on first subscription for a missing key, dedup-joins any
1108
+ * concurrent fetch, and emits the stored value thereafter.
1109
+ * - `invalidate(key)`: stale-while-revalidate — keeps the current
1110
+ * value visible to observers, clears the in-flight guard, starts a
1111
+ * fresh fetch. If the previous fetch was orphaned (SSE torn down,
1112
+ * response lost), this is how the cache recovers.
1113
+ * - `remove(key)`: drops the cache entry entirely. Used for entity
1114
+ * deletions (B13a). No refetch.
1115
+ * - `set(key, value)`: writes through without a fetch. Used when a
1116
+ * bus event carries the new value inline (B13b).
1117
+ * - `invalidateAll()`: per-key SWR refetch of every currently-cached
1118
+ * entry. Used by gap-detection paths.
1119
+ * - `dispose()`: completes the store so observers unsubscribe.
1120
+ *
1121
+ * What's deliberately out:
1122
+ * - No subscriber ref-counting / GC of unobserved keys. The per-key
1123
+ * observable memo grows with the set of observed keys for the cache's
1124
+ * lifetime (B11). Acceptable given cache lifetime == client lifetime.
1125
+ * - No TTL / cacheTime. Entries are evicted only by explicit remove.
1126
+ * - No retry / backoff. A failing fetch leaves the cache unchanged
1127
+ * (B6); the caller drives retry via invalidate.
1128
+ */
1129
+
1130
+ interface Cache<K, V> {
1131
+ /** Observable stream of the value at `key`. Triggers a fetch if not cached. */
1132
+ observe(key: K): Observable<V | undefined>;
1133
+ /** Synchronous snapshot of the current value, without triggering a fetch. */
1134
+ get(key: K): V | undefined;
1135
+ /** Iterator of currently-cached keys. For invalidateAll and diagnostics. */
1136
+ keys(): K[];
1137
+ /**
1138
+ * Mark the entry stale and refetch. Keeps the previous value visible
1139
+ * to observers during the refetch (stale-while-revalidate).
1140
+ */
1141
+ invalidate(key: K): void;
1142
+ /** Drop the entry from the cache. No refetch. */
1143
+ remove(key: K): void;
1144
+ /** Write-through: set the value directly without a fetch. */
1145
+ set(key: K, value: V): void;
1146
+ /** Per-key SWR refetch of every currently-cached entry. */
1147
+ invalidateAll(): void;
1148
+ /** Release the underlying subject. Observers complete. */
1149
+ dispose(): void;
1150
+ }
1151
+ declare function createCache<K, V>(fetchFn: (key: K) => Promise<V>): Cache<K, V>;
1152
+
1153
+ /**
1154
+ * KnowledgeBase — a connection to a Semiont backend instance.
1155
+ *
1156
+ * Each KB has its own JWT, its own API base URL, and its own session.
1157
+ * The user is "authenticated against KB X" — never globally authenticated.
1158
+ */
1159
+ interface KnowledgeBase {
1160
+ id: string;
1161
+ label: string;
1162
+ host: string;
1163
+ port: number;
1164
+ protocol: 'http' | 'https';
1165
+ email: string;
1166
+ gitBranch?: string;
1167
+ }
1168
+ /**
1169
+ * Input shape for adding a new KB. The id is generated by the provider.
1170
+ */
1171
+ type NewKnowledgeBase = Omit<KnowledgeBase, 'id'>;
1172
+ /**
1173
+ * Status of the locally-stored credential for a KB. Derived from the
1174
+ * presence and validity of the JWT in session storage.
1175
+ */
1176
+ type KbSessionStatus = 'authenticated' | 'expired' | 'signed-out' | 'unreachable';
1177
+
1178
+ /**
1179
+ * Session-level error surface. Emitted on `SemiontBrowser.error$` for
1180
+ * failures that make the session itself unusable (auth failed, actor
1181
+ * couldn't start, token refresh terminally exhausted). Per-request
1182
+ * errors stay with the caller as normal Promise rejections.
1183
+ */
1184
+ type SemiontErrorCode = 'session.construct-failed' | 'session.auth-failed' | 'session.refresh-exhausted' | 'browser.sign-in-failed';
1185
+ declare class SemiontError extends Error {
1186
+ readonly code: SemiontErrorCode;
1187
+ readonly kbId: string | null;
1188
+ constructor(code: SemiontErrorCode, message: string, kbId?: string | null);
1189
+ }
1190
+
1191
+ /**
1192
+ * SessionStorage — environment-agnostic persistence adapter for the
1193
+ * Semiont session layer. Decouples `SemiontSession` / `SemiontBrowser`
1194
+ * from `localStorage` / `window` so the same classes can run in a
1195
+ * browser, a CLI process, or tests without environment guards.
1196
+ *
1197
+ * Implementations shipped here:
1198
+ * - `InMemorySessionStorage` — map-backed, for tests.
1199
+ *
1200
+ * Browser-backed (`WebBrowserStorage`) lives in `@semiont/react-ui`
1201
+ * because it touches browser-only globals.
1202
+ */
1203
+ /** String key/value store with optional cross-context change subscription. */
1204
+ interface SessionStorage {
1205
+ /** Read a string value; null if absent. */
1206
+ get(key: string): string | null;
1207
+ /** Write a string value. */
1208
+ set(key: string, value: string): void;
1209
+ /** Remove a key. No-op if absent. */
1210
+ delete(key: string): void;
1211
+ /**
1212
+ * Optional: subscribe to external changes (cross-tab, cross-process).
1213
+ * Browser implements via the `storage` event; filesystem would use
1214
+ * fs.watch; in-memory omits this method. Returns an unsubscribe
1215
+ * callback. If omitted, cross-context sync simply isn't available
1216
+ * in that environment — the session still works correctly within a
1217
+ * single process.
1218
+ */
1219
+ subscribe?(handler: (key: string, newValue: string | null) => void): () => void;
1220
+ }
1221
+ /**
1222
+ * Map-backed `SessionStorage`. Cross-context sync is not implemented;
1223
+ * tests that need it can drive it manually.
1224
+ */
1225
+ declare class InMemorySessionStorage implements SessionStorage {
1226
+ private readonly map;
1227
+ get(key: string): string | null;
1228
+ set(key: string, value: string): void;
1229
+ delete(key: string): void;
1230
+ }
1231
+
1232
+ /**
1233
+ * SemiontSession — per-backend session lifetime object. Owns the
1234
+ * SemiontApiClient, the access token BehaviorSubject, and optionally
1235
+ * an authenticated user. One SemiontSession exists per active backend
1236
+ * connection; lifetime is decoupled from React mount lifetime.
1237
+ *
1238
+ * Headless by design. Runs in browsers, CLIs, workers, and tests.
1239
+ * UI-specific state (session-expired/permission-denied modals) lives
1240
+ * in `FrontendSessionSignals`, which wraps a session — the session
1241
+ * itself has no modal observables, no user-facing notifications.
1242
+ *
1243
+ * Auth is parameterized via callbacks passed at construction:
1244
+ *
1245
+ * - `refresh()` — invoked on 401 / proactive re-auth. Returns the
1246
+ * new access token, or null on failure. The frontend passes a
1247
+ * closure that runs the refresh-token flow; the worker passes
1248
+ * one that exchanges the shared secret.
1249
+ *
1250
+ * - `validate(token)` — optional. If provided, the session calls
1251
+ * it once at startup with the stored token to confirm it's
1252
+ * still good and populate `user$`. Frontend passes `getMe`;
1253
+ * worker omits this (service principals have no user record).
1254
+ *
1255
+ * - `onAuthFailed(message)` — optional. Invoked when refresh
1256
+ * terminally fails (expired token, no recovery possible). The
1257
+ * frontend wires this to `FrontendSessionSignals.notifySessionExpired`
1258
+ * so the modal surfaces; headless consumers typically just log.
1259
+ *
1260
+ * Persistence goes through a `SessionStorage` adapter provided at
1261
+ * construction — the session never touches `localStorage` or `window`
1262
+ * directly.
1263
+ */
1264
+
1265
+ type UserInfo = components['schemas']['UserResponse'];
1266
+ interface SemiontSessionConfig {
1267
+ kb: KnowledgeBase;
1268
+ /** Persistence adapter. Reads/writes tokens via this. */
1269
+ storage: SessionStorage;
1270
+ /**
1271
+ * Re-authenticate after expiry / 401. Returns a new access token
1272
+ * (no "Bearer " prefix) on success, or null if recovery is
1273
+ * impossible (user needs to sign in again, or the service secret
1274
+ * is missing). Callers must dedupe concurrent invocations if the
1275
+ * underlying auth call isn't itself idempotent.
1276
+ */
1277
+ refresh: () => Promise<string | null>;
1278
+ /**
1279
+ * Validate the stored token at startup and populate `user$`. Omit
1280
+ * for service-principal sessions (worker, CLI tools) where there
1281
+ * is no user record to fetch.
1282
+ */
1283
+ validate?: (token: AccessToken) => Promise<UserInfo | null>;
1284
+ /**
1285
+ * Invoked when refresh terminally fails. Frontend consumers wire
1286
+ * this to a UI signal that surfaces the session-expired modal.
1287
+ */
1288
+ onAuthFailed?: (message: string | null) => void;
1289
+ /** Called for session-level failures (auth, refresh exhaustion). */
1290
+ onError?: (err: SemiontError) => void;
1291
+ }
1292
+ declare class SemiontSession {
1293
+ readonly kb: KnowledgeBase;
1294
+ readonly client: SemiontApiClient;
1295
+ readonly token$: BehaviorSubject<AccessToken | null>;
1296
+ readonly user$: BehaviorSubject<UserInfo | null>;
1297
+ readonly streamState$: Observable<ConnectionState>;
1298
+ /** Resolves after the initial validation round-trip completes (success or failure). */
1299
+ readonly ready: Promise<void>;
1300
+ private readonly storage;
1301
+ private readonly doRefresh;
1302
+ private readonly doValidate?;
1303
+ private readonly onAuthFailed;
1304
+ private readonly onError;
1305
+ private refreshTimer;
1306
+ private unsubscribeStorage;
1307
+ private disposed;
1308
+ constructor(config: SemiontSessionConfig);
1309
+ /**
1310
+ * Run the initial mount-time validation. If a stored access token is
1311
+ * present and unexpired, call the configured `validate` with it to
1312
+ * confirm it still works and populate `user$`. If expired, try
1313
+ * refresh first. On 401 from validate, try refresh once. Surfaces
1314
+ * auth-failed on terminal failure.
1315
+ *
1316
+ * When no `validate` callback is provided (service principals), this
1317
+ * still runs through the refresh-if-expired step so the stored
1318
+ * token is current — it just skips the user-validation round trip.
1319
+ */
1320
+ private validate;
1321
+ /**
1322
+ * Refresh the access token via the configured `refresh` callback.
1323
+ * On success, pushes the new token into `token$` and schedules the
1324
+ * next proactive refresh. On failure, clears persisted state and
1325
+ * fires `onAuthFailed` — the frontend's wiring of that callback is
1326
+ * what surfaces the session-expired modal.
1327
+ */
1328
+ refresh(): Promise<AccessToken | null>;
1329
+ private scheduleProactiveRefresh;
1330
+ private clearRefreshTimer;
1331
+ /**
1332
+ * Cross-context sync: another tab/process refreshed or signed out this
1333
+ * KB. Mirror the change into our in-memory state.
1334
+ */
1335
+ private handleStorageChange;
1336
+ get expiresAt(): Date | null;
1337
+ dispose(): Promise<void>;
1338
+ }
1339
+
1340
+ /**
1341
+ * OpenResource — a single entry in the open-resources list (tabs).
1342
+ *
1343
+ * The list itself lives on `SemiontBrowser.openResources$`. The CRUD
1344
+ * methods (`addOpenResource`, `removeOpenResource`, `updateOpenResourceName`,
1345
+ * `reorderOpenResources`) live on `SemiontBrowser` too.
1346
+ */
1347
+ interface OpenResource {
1348
+ /** Unique identifier for the resource */
1349
+ id: string;
1350
+ /** Display name of the resource */
1351
+ name: string;
1352
+ /** Timestamp when the resource was opened */
1353
+ openedAt: number;
1354
+ /** Order/position for manual sorting (optional for backward compatibility) */
1355
+ order?: number;
1356
+ /** Media type for icon display (e.g., 'application/pdf', 'text/plain') */
1357
+ mediaType?: string;
1358
+ /** Working-tree URI (e.g. "file://docs/overview.md") — used as tooltip in navigation */
1359
+ storageUri?: string;
1360
+ }
1361
+
1362
+ /**
1363
+ * FrontendSessionSignals — modal state that belongs to the UI, not
1364
+ * the session itself.
1365
+ *
1366
+ * `SemiontSession` is a headless per-backend client + token + user
1367
+ * holder. It can run in any process: browser, worker, CLI, test. But
1368
+ * the session-expired / permission-denied *modals* only make sense
1369
+ * in a UI context. Keeping those observables on `SemiontSession`
1370
+ * meant workers and CLIs carried four dead BehaviorSubjects that
1371
+ * nothing would ever fire.
1372
+ *
1373
+ * `FrontendSessionSignals` owns the modal state and has no hard
1374
+ * reference to a session. `SemiontBrowser` constructs one alongside
1375
+ * every frontend session and wires:
1376
+ *
1377
+ * - `session.onAuthFailed` → `signals.notifySessionExpired` so
1378
+ * proactive-refresh failures surface as modals
1379
+ * - `notify` module handlers → the active signals' methods so
1380
+ * external callers (e.g. React Query's QueryCache.onError) can
1381
+ * trigger the modals without touching the session
1382
+ *
1383
+ * React consumers that need modal state subscribe here; consumers
1384
+ * that need bus/HTTP access continue to subscribe to the session.
1385
+ *
1386
+ * Session auth-state cleanup (clearing token, clearing storage) is
1387
+ * the session's own responsibility inside `refresh()` — by the time
1388
+ * `notifySessionExpired` runs, the session has already torn down.
1389
+ * Signals only surfaces the modal.
1390
+ */
1391
+
1392
+ declare class FrontendSessionSignals {
1393
+ readonly sessionExpiredAt$: BehaviorSubject<number | null>;
1394
+ readonly sessionExpiredMessage$: BehaviorSubject<string | null>;
1395
+ readonly permissionDeniedAt$: BehaviorSubject<number | null>;
1396
+ readonly permissionDeniedMessage$: BehaviorSubject<string | null>;
1397
+ constructor();
1398
+ notifySessionExpired(message: string | null): void;
1399
+ notifyPermissionDenied(message: string | null): void;
1400
+ acknowledgeSessionExpired(): void;
1401
+ acknowledgePermissionDenied(): void;
1402
+ dispose(): void;
1403
+ }
1404
+
1405
+ /**
1406
+ * SemiontBrowser — top-level app-facing container for non-KB state.
1407
+ *
1408
+ * Holds the list of configured KBs, the active KB selection, the active
1409
+ * SemiontSession, the identity token, the open-resources list, and a
1410
+ * session-level error stream. Module-scoped singleton — survives every
1411
+ * React re-render, remount, and route change. `SemiontProvider` hands
1412
+ * the singleton to the React tree; `useSemiont()` returns it.
1413
+ *
1414
+ * Persistence goes through a `SessionStorage` adapter provided at
1415
+ * construction — the browser never touches `localStorage` or `window`
1416
+ * directly.
1417
+ */
1418
+
1419
+ interface SemiontBrowserConfig {
1420
+ /** Persistence adapter. The browser reads/writes all persisted state via this. */
1421
+ storage: SessionStorage;
1422
+ }
1423
+ declare class SemiontBrowser {
1424
+ readonly kbs$: BehaviorSubject<KnowledgeBase[]>;
1425
+ readonly activeKbId$: BehaviorSubject<string | null>;
1426
+ readonly activeSession$: BehaviorSubject<SemiontSession | null>;
1427
+ /**
1428
+ * Modal signals (session-expired / permission-denied) for the
1429
+ * currently-active session. Parallels `activeSession$` — always
1430
+ * non-null when `activeSession$` is non-null, always null when it
1431
+ * is. Extracted from the session itself so headless sessions
1432
+ * (workers, CLIs, tests) don't carry dead modal observables.
1433
+ * See [FrontendSessionSignals](./frontend-session-signals.ts).
1434
+ */
1435
+ readonly activeSignals$: BehaviorSubject<FrontendSessionSignals | null>;
1436
+ /**
1437
+ * True while a session is actively being constructed (setActiveKb /
1438
+ * signIn in flight, awaiting `session.ready`). Distinguishes the
1439
+ * "session about to arrive" intermediate state from "session
1440
+ * intentionally null" (after signOut, or when the active KB has no
1441
+ * stored credentials). UIs that want a loading spinner should gate
1442
+ * on this; otherwise they get stuck spinning after every signOut.
1443
+ */
1444
+ readonly sessionActivating$: BehaviorSubject<boolean>;
1445
+ readonly openResources$: BehaviorSubject<OpenResource[]>;
1446
+ readonly error$: Subject<SemiontError>;
1447
+ readonly identityToken$: BehaviorSubject<string | null>;
1448
+ private readonly storage;
1449
+ /**
1450
+ * App-scoped EventBus. Hosts UI-shell events that must work regardless
1451
+ * of whether a KB session is active: panel toggles, sidebar state,
1452
+ * tab reorders, routing, settings, etc. Disjoint from the per-session
1453
+ * bus inside `SemiontApiClient`, which carries KB-content events
1454
+ * (mark:*, beckon:*, gather:*, match:*, bind:*, yield:*, browse:click).
1455
+ */
1456
+ private readonly eventBus;
1457
+ private unregisterNotify;
1458
+ private unsubscribeStorage;
1459
+ private disposed;
1460
+ private activating;
1461
+ /**
1462
+ * Per-KB in-flight refresh dedup. Simultaneous 401s for the same
1463
+ * KB converge on a single `/api/tokens/refresh` network call.
1464
+ * Was previously module-scoped in `refresh.ts`; moved here when
1465
+ * that file was deleted — SemiontBrowser is a singleton so the
1466
+ * scoping is equivalent.
1467
+ */
1468
+ private readonly inFlightRefreshes;
1469
+ constructor(config: SemiontBrowserConfig);
1470
+ /** Emit an event on the browser's app-scoped bus. */
1471
+ emit<K extends keyof EventMap>(channel: K, payload: EventMap[K]): void;
1472
+ /** Subscribe to an event; returns unsubscribe. */
1473
+ on<K extends keyof EventMap>(channel: K, handler: (payload: EventMap[K]) => void): () => void;
1474
+ /** Read-only observable for an app-scoped channel. */
1475
+ stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]>;
1476
+ /**
1477
+ * Set the app-level identity token (from NextAuth's useSession).
1478
+ * Called at the root layout via a single `useEffect`. No other site
1479
+ * in the codebase should call this.
1480
+ */
1481
+ setIdentityToken(token: string | null): void;
1482
+ addKb(input: NewKnowledgeBase, access: string, refresh: string): KnowledgeBase;
1483
+ removeKb(id: string): void;
1484
+ updateKb(id: string, updates: Partial<KnowledgeBase>): void;
1485
+ /**
1486
+ * Read the locally-stored credential status for a KB. Pure / synchronous —
1487
+ * does not subscribe to context changes. Used by KB-list UI to color status
1488
+ * dots without requiring re-renders on every tick.
1489
+ */
1490
+ getKbSessionStatus(kbId: string): KbSessionStatus;
1491
+ /**
1492
+ * Switch the active KB. Follows the D2 disposal contract:
1493
+ * 1. Synchronously announce the new id on `activeKbId$` and null out
1494
+ * `activeSession$` so views see a safe empty state first.
1495
+ * 2. Serialize overlapping calls — if an activation is in flight, wait
1496
+ * for it before proceeding.
1497
+ * 3. Dispose whatever session is currently live.
1498
+ * 4. Construct the next session and await `session.ready`.
1499
+ * 5. Before emitting, re-check `activeKbId$` — if a newer call superseded
1500
+ * us while we waited, dispose our session and skip the emit.
1501
+ * 6. Emit the new session.
1502
+ */
1503
+ setActiveKb(id: string | null): Promise<void>;
1504
+ /**
1505
+ * Sign in to an existing KB: store the tokens and (re)activate the
1506
+ * session. If the KB is already active, the current session is disposed
1507
+ * and replaced so the new tokens take effect.
1508
+ */
1509
+ signIn(id: string, access: string, refresh: string): Promise<void>;
1510
+ /**
1511
+ * Sign out of a KB: clear stored tokens. If the KB is active, dispose
1512
+ * its session + signals and emit null for both.
1513
+ */
1514
+ signOut(id: string): Promise<void>;
1515
+ addOpenResource(id: string, name: string, mediaType?: string, storageUri?: string): void;
1516
+ removeOpenResource(id: string): void;
1517
+ updateOpenResourceName(id: string, name: string): void;
1518
+ reorderOpenResources(oldIndex: number, newIndex: number): void;
1519
+ /**
1520
+ * Refresh the active KB's access token. Returns the new token on
1521
+ * success, null on failure. Concurrent calls for the same KB
1522
+ * dedupe through `inFlightRefreshes`, so simultaneous 401s trigger
1523
+ * only one `/api/tokens/refresh` round trip.
1524
+ *
1525
+ * Uses a throwaway `SemiontApiClient` with no `tokenRefresher` —
1526
+ * a refresh call returning 401 would otherwise re-enter this
1527
+ * function infinitely.
1528
+ */
1529
+ private performRefresh;
1530
+ /**
1531
+ * Validate an access token by calling `getMe` on a throwaway
1532
+ * client. The session uses this once at startup to populate
1533
+ * `user$`; 401 triggers a refresh-then-retry inside the session.
1534
+ */
1535
+ private performValidate;
1536
+ dispose(): Promise<void>;
1537
+ }
1538
+
1539
+ /**
1540
+ * Module-scoped singleton for SemiontBrowser. Constructed lazily on first
1541
+ * `getBrowser()` call, survives every React re-render, remount, and route
1542
+ * change.
1543
+ *
1544
+ * The caller provides a `SessionStorage` implementation — there is no
1545
+ * default here because storage-backend selection is environment-specific
1546
+ * (browsers use `WebBrowserStorage`, CLI uses a filesystem adapter,
1547
+ * tests use `InMemorySessionStorage`). The first call to `getBrowser`
1548
+ * wins; subsequent calls return the cached instance regardless of the
1549
+ * storage passed.
1550
+ */
1551
+
1552
+ interface GetBrowserOptions {
1553
+ /** Persistence adapter used to construct the singleton on first call. */
1554
+ storage: SessionStorage;
1555
+ }
1556
+ declare function getBrowser(options: GetBrowserOptions): SemiontBrowser;
1557
+
1558
+ /**
1559
+ * Pure helpers and storage-adapter-driven loaders for the Semiont
1560
+ * session layer.
1561
+ *
1562
+ * Contains:
1563
+ * - Storage key shape (constants, `sessionKey(kbId)`)
1564
+ * - JWT expiry parsing and "is expired" check
1565
+ * - URL/protocol helpers for KB instances
1566
+ * - Loaders/savers that take a `SessionStorage` and operate over it
1567
+ * (no direct `localStorage` access)
1568
+ *
1569
+ * No React imports, no module-scoped state, no side effects beyond
1570
+ * whatever the passed-in `SessionStorage` does.
1571
+ */
1572
+
1573
+ /** The shape persisted per KB. */
1574
+ interface StoredSession {
1575
+ access: string;
1576
+ refresh: string;
1577
+ }
1578
+ declare function setStoredSession(storage: SessionStorage, kbId: string, session: StoredSession): void;
1579
+ declare function defaultProtocol(host: string): 'http' | 'https';
1580
+ declare function isValidHostname(host: string): boolean;
1581
+ declare function kbBackendUrl(kb: KnowledgeBase): string;
1582
+
1583
+ declare function notifySessionExpired(message?: string): void;
1584
+ declare function notifyPermissionDenied(message?: string): void;
1585
+
1586
+ /**
1587
+ * createSearchPipeline
1588
+ *
1589
+ * A debounced-search RxJS pipeline factory. Combines an input Subject with a
1590
+ * downstream fetch function and emits typed `{ results, isSearching }` state.
1591
+ *
1592
+ * Designed to be created once per component instance via React's
1593
+ * `useState(() => createSearchPipeline(...))` lazy initializer, then consumed
1594
+ * via `useObservable(pipeline.state$)`. The pipeline holds no React state and
1595
+ * has no React imports — it's pure RxJS, unit-testable without a renderer.
1596
+ *
1597
+ * The fetch function is expected to return `Observable<T[] | undefined>`,
1598
+ * matching the cache-miss-then-data shape of `BrowseNamespace` Observables:
1599
+ * `undefined` means "fetch in flight, no value yet"; an array means "data
1600
+ * available (possibly empty)".
1601
+ */
1602
+
1603
+ interface SearchState<T> {
1604
+ results: T[];
1605
+ isSearching: boolean;
1606
+ }
1607
+ interface SearchPipeline<T> {
1608
+ /** Latest query string. Bind to a controlled input via `useObservable`. */
1609
+ query$: Observable<string>;
1610
+ /** Latest search state — results plus a loading flag. */
1611
+ state$: Observable<SearchState<T>>;
1612
+ /** Push a new query value. Triggers the debounced fetch. */
1613
+ setQuery(value: string): void;
1614
+ /** Tear down the input Subject. Call from `useEffect` cleanup. */
1615
+ dispose(): void;
1616
+ }
1617
+ interface SearchPipelineOptions {
1618
+ /** Milliseconds to wait after the last keystroke before fetching. Default 250. */
1619
+ debounceMs?: number;
1620
+ /** Initial query value. Useful for modals that open with a pre-filled term. */
1621
+ initialQuery?: string;
1622
+ }
1623
+ declare function createSearchPipeline<T>(fetch: (query: string) => Observable<T[] | undefined>, options?: SearchPipelineOptions): SearchPipeline<T>;
1624
+
1625
+ interface BeckonVM extends ViewModel {
1626
+ hoveredAnnotationId$: Observable<string | null>;
1627
+ hover(annotationId: string | null): void;
1628
+ focus(annotationId: string): void;
1629
+ sparkle(annotationId: string): void;
1630
+ }
1631
+ declare function createBeckonVM(client: SemiontApiClient): BeckonVM;
1632
+ /** Default milliseconds the mouse must dwell before beckon:hover is emitted. */
1633
+ declare const HOVER_DELAY_MS = 150;
1634
+ type EmitHover = (annotationId: string | null) => void;
1635
+ interface HoverHandlers {
1636
+ handleMouseEnter: (annotationId: string) => void;
1637
+ handleMouseLeave: () => void;
1638
+ cleanup: () => void;
1639
+ }
1640
+ declare function createHoverHandlers(emit: EmitHover, delayMs: number): HoverHandlers;
1641
+
1642
+ /**
1643
+ * ShellVM — app-shell state: which toolbar panel is open, tab-bar
1644
+ * coordination helpers, scroll-to-annotation signals. Lives on
1645
+ * `SemiontBrowser`'s app-scoped bus (not the per-session client bus)
1646
+ * because panel toggles and shell chrome must work regardless of
1647
+ * whether a KB session is active.
1648
+ *
1649
+ * Channels: `panel:toggle`, `panel:open`, `panel:close`.
1650
+ */
1651
+
1652
+ type ToolbarPanelType = 'history' | 'info' | 'annotations' | 'settings' | 'collaboration' | 'user' | 'jsonld' | 'knowledge-base';
1653
+ declare const COMMON_PANELS: readonly ToolbarPanelType[];
1654
+ declare const RESOURCE_PANELS: readonly ToolbarPanelType[];
1655
+ interface ShellVM extends ViewModel {
1656
+ activePanel$: Observable<ToolbarPanelType | null>;
1657
+ scrollToAnnotationId$: Observable<string | null>;
1658
+ panelInitialTab$: Observable<{
1659
+ tab: string;
1660
+ generation: number;
1661
+ } | null>;
1662
+ openPanel(panel: string): void;
1663
+ closePanel(): void;
1664
+ togglePanel(panel: string): void;
1665
+ onScrollCompleted(): void;
1666
+ }
1667
+ interface ShellVMOptions {
1668
+ initialPanel?: ToolbarPanelType | null;
1669
+ onPanelChange?: (panel: ToolbarPanelType | null) => void;
1670
+ }
1671
+ declare function createShellVM(browser: SemiontBrowser, options?: ShellVMOptions): ShellVM;
1672
+
1673
+ interface GatherVM extends ViewModel {
1674
+ context$: Observable<GatheredContext | null>;
1675
+ loading$: Observable<boolean>;
1676
+ error$: Observable<Error | null>;
1677
+ annotationId$: Observable<AnnotationId | null>;
1678
+ }
1679
+ declare function createGatherVM(client: SemiontApiClient, resourceId: ResourceId): GatherVM;
1680
+
1681
+ interface MatchVM extends ViewModel {
1682
+ }
1683
+ declare function createMatchVM(client: SemiontApiClient, _resourceId: ResourceId): MatchVM;
1684
+
1685
+ type JobProgress$1 = components['schemas']['JobProgress'];
1686
+ interface GenerateDocumentOptions {
1687
+ title: string;
1688
+ storageUri: string;
1689
+ prompt?: string;
1690
+ language?: string;
1691
+ temperature?: number;
1692
+ maxTokens?: number;
1693
+ context: GatheredContext;
1694
+ }
1695
+ interface YieldVM extends ViewModel {
1696
+ isGenerating$: Observable<boolean>;
1697
+ progress$: Observable<JobProgress$1 | null>;
1698
+ generate(referenceId: string, options: GenerateDocumentOptions): void;
1699
+ }
1700
+ declare function createYieldVM(client: SemiontApiClient, resourceId: ResourceId, locale: string): YieldVM;
1701
+
1702
+ type JobProgress = components['schemas']['JobProgress'];
1703
+ interface PendingAnnotation {
1704
+ selector: Selector | Selector[];
1705
+ motivation: Motivation;
1706
+ }
1707
+ interface MarkVM extends ViewModel {
1708
+ pendingAnnotation$: Observable<PendingAnnotation | null>;
1709
+ assistingMotivation$: Observable<Motivation | null>;
1710
+ progress$: Observable<JobProgress | null>;
1711
+ }
1712
+ declare function createMarkVM(client: SemiontApiClient, resourceId: ResourceId): MarkVM;
1713
+
1714
+ type ResourceDescriptor$2 = components['schemas']['ResourceDescriptor'];
1715
+ interface DiscoverVM extends ViewModel {
1716
+ browse: ShellVM;
1717
+ search: SearchPipeline<ResourceDescriptor$2>;
1718
+ recentResources$: Observable<ResourceDescriptor$2[]>;
1719
+ entityTypes$: Observable<string[]>;
1720
+ isLoadingRecent$: Observable<boolean>;
1721
+ }
1722
+ declare function createDiscoverVM(client: SemiontApiClient, browse: ShellVM): DiscoverVM;
1723
+
1724
+ interface EntityTagsVM extends ViewModel {
1725
+ browse: ShellVM;
1726
+ entityTypes$: Observable<string[]>;
1727
+ isLoading$: Observable<boolean>;
1728
+ newTag$: Observable<string>;
1729
+ error$: Observable<string>;
1730
+ isAdding$: Observable<boolean>;
1731
+ setNewTag(value: string): void;
1732
+ addTag(): Promise<void>;
1733
+ }
1734
+ declare function createEntityTagsVM(client: SemiontApiClient, browse: ShellVM): EntityTagsVM;
1735
+
1736
+ interface ImportPreview {
1737
+ format: string;
1738
+ version: number;
1739
+ sourceUrl: string;
1740
+ stats: Record<string, number>;
1741
+ }
1742
+ interface ExchangeVM extends ViewModel {
1743
+ browse: ShellVM;
1744
+ selectedFile$: Observable<File | null>;
1745
+ preview$: Observable<ImportPreview | null>;
1746
+ importPhase$: Observable<string | null>;
1747
+ importMessage$: Observable<string | undefined>;
1748
+ importResult$: Observable<Record<string, unknown> | undefined>;
1749
+ isExporting$: Observable<boolean>;
1750
+ isImporting$: Observable<boolean>;
1751
+ selectFile(file: File): void;
1752
+ cancelImport(): void;
1753
+ doExport(): Promise<{
1754
+ blob: Blob;
1755
+ filename: string;
1756
+ }>;
1757
+ doImport(): Promise<void>;
1758
+ }
1759
+ declare function createExchangeVM(browse: ShellVM, exportFn: (params?: {
1760
+ includeArchived?: boolean;
1761
+ }) => Promise<Response>, importFn: (file: File, options?: {
1762
+ onProgress?: (event: {
1763
+ phase: string;
1764
+ message?: string;
1765
+ result?: Record<string, unknown>;
1766
+ }) => void;
1767
+ }) => Promise<{
1768
+ phase: string;
1769
+ message?: string;
1770
+ result?: Record<string, unknown>;
1771
+ }>): ExchangeVM;
1772
+
1773
+ interface AdminUsersVM extends ViewModel {
1774
+ browse: ShellVM;
1775
+ users$: Observable<unknown[]>;
1776
+ stats$: Observable<unknown | null>;
1777
+ usersLoading$: Observable<boolean>;
1778
+ statsLoading$: Observable<boolean>;
1779
+ updateUser(id: string, data: {
1780
+ isAdmin?: boolean;
1781
+ isActive?: boolean;
1782
+ }): Promise<void>;
1783
+ }
1784
+ declare function createAdminUsersVM(client: SemiontApiClient, browse: ShellVM): AdminUsersVM;
1785
+
1786
+ interface AdminSecurityVM extends ViewModel {
1787
+ browse: ShellVM;
1788
+ providers$: Observable<unknown[]>;
1789
+ allowedDomains$: Observable<string[]>;
1790
+ isLoading$: Observable<boolean>;
1791
+ }
1792
+ declare function createAdminSecurityVM(client: SemiontApiClient, browse: ShellVM): AdminSecurityVM;
1793
+
1794
+ interface WelcomeVM extends ViewModel {
1795
+ userData$: Observable<{
1796
+ termsAcceptedAt?: string;
1797
+ } | null>;
1798
+ isProcessing$: Observable<boolean>;
1799
+ acceptTerms(): Promise<void>;
1800
+ }
1801
+ declare function createWelcomeVM(client: SemiontApiClient): WelcomeVM;
1802
+
1803
+ type ResourceDescriptor$1 = components['schemas']['ResourceDescriptor'];
1804
+ interface ResourceLoaderVM extends ViewModel {
1805
+ resource$: Observable<ResourceDescriptor$1 | undefined>;
1806
+ isLoading$: Observable<boolean>;
1807
+ invalidate(): void;
1808
+ }
1809
+ declare function createResourceLoaderVM(client: SemiontApiClient, resourceId: ResourceId): ResourceLoaderVM;
1810
+
1811
+ interface SessionVM extends ViewModel {
1812
+ isLoggingOut$: Observable<boolean>;
1813
+ logout(): Promise<void>;
1814
+ }
1815
+ declare function createSessionVM(client: SemiontApiClient): SessionVM;
1816
+
1817
+ interface SmelterEvent {
1818
+ type: string;
1819
+ resourceId?: string;
1820
+ payload: Record<string, unknown>;
1821
+ }
1822
+ interface SmelterActorVMOptions {
1823
+ baseUrl: string;
1824
+ token: string;
1825
+ reconnectMs?: number;
1826
+ }
1827
+ interface SmelterActorVM extends ViewModel {
1828
+ events$: Observable<SmelterEvent>;
1829
+ state$: Observable<ConnectionState>;
1830
+ emit(channel: string, payload: Record<string, unknown>): Promise<void>;
1831
+ start(): void;
1832
+ stop(): void;
1833
+ }
1834
+ declare function createSmelterActorVM(options: SmelterActorVMOptions): SmelterActorVM;
1835
+
1836
+ /**
1837
+ * Job Claim Adapter — worker-side job lifecycle glue on top of a
1838
+ * shared `ActorVM`.
1839
+ *
1840
+ * Replaces the old `WorkerVM`, which owned its own actor and
1841
+ * duplicated the SSE connection that `SemiontApiClient` already held.
1842
+ * Workers now construct a `SemiontSession` normally (one actor, one
1843
+ * SSE connection) and use this adapter to attach job-claim behaviour
1844
+ * on top of `session.client.actor`.
1845
+ *
1846
+ * The adapter is intentionally thin: it subscribes to `job:queued`
1847
+ * on the actor, claims jobs via the existing request-response
1848
+ * protocol (`job:claim` → `job:claimed` / `job:claim-failed`), and
1849
+ * exposes observables for job orchestration. It does **not** own
1850
+ * the actor, has no HTTP concerns, and has no modal state.
1851
+ */
1852
+
1853
+ interface JobAssignment {
1854
+ jobId: string;
1855
+ type: string;
1856
+ resourceId: string;
1857
+ }
1858
+ interface ActiveJob {
1859
+ jobId: string;
1860
+ type: string;
1861
+ resourceId: string;
1862
+ userId: string;
1863
+ params: Record<string, unknown>;
1864
+ }
1865
+ interface JobClaimAdapterOptions {
1866
+ /** Shared actor (typically `session.client.actor`). */
1867
+ actor: ActorVM;
1868
+ /**
1869
+ * Job types this worker can process. Jobs of other types that
1870
+ * arrive on `job:queued` are ignored. Empty array = accept any.
1871
+ */
1872
+ jobTypes: string[];
1873
+ }
1874
+ interface JobClaimAdapter {
1875
+ /** Currently-claimed job, or null when idle. */
1876
+ readonly activeJob$: Observable<ActiveJob | null>;
1877
+ /** True while a claim is in flight or a job is being processed. */
1878
+ readonly isProcessing$: Observable<boolean>;
1879
+ /** Monotonically-incrementing count of successfully-completed jobs. */
1880
+ readonly jobsCompleted$: Observable<number>;
1881
+ /** Stream of job failures (including claim-failed and processing errors). */
1882
+ readonly errors$: Observable<{
1883
+ jobId: string;
1884
+ error: string;
1885
+ }>;
1886
+ /**
1887
+ * Subscribe to `job:queued` events (adding the channel to the actor
1888
+ * if not already subscribed) and begin claiming matching jobs.
1889
+ * Idempotent — calling `start()` twice is a no-op.
1890
+ */
1891
+ start(): void;
1892
+ /** Stop claiming new jobs. Does not cancel an in-flight job. */
1893
+ stop(): void;
1894
+ /** Signal successful completion of `activeJob$`. */
1895
+ completeJob(): void;
1896
+ /** Signal failure of `activeJob$`. Emits on `errors$`. */
1897
+ failJob(jobId: string, error: string): void;
1898
+ /** Release observables. Does not dispose the shared actor. */
1899
+ dispose(): void;
1900
+ }
1901
+ /**
1902
+ * Attach job-claim behaviour to a shared `ActorVM`.
1903
+ */
1904
+ declare function createJobClaimAdapter(options: JobClaimAdapterOptions): JobClaimAdapter;
1905
+
1906
+ interface Job {
1907
+ jobId: string;
1908
+ type: string;
1909
+ status: string;
1910
+ resourceId: string;
1911
+ userId: string;
1912
+ created: string;
1913
+ startedAt?: string;
1914
+ completedAt?: string;
1915
+ error?: string;
1916
+ progress?: Record<string, unknown>;
1917
+ result?: Record<string, unknown>;
1918
+ }
1919
+ interface JobQueueVM extends ViewModel {
1920
+ jobs$: Observable<Job[]>;
1921
+ pendingByType$: Observable<Map<string, number>>;
1922
+ runningJobs$: Observable<Job[]>;
1923
+ jobCreated$: Observable<Job>;
1924
+ jobCompleted$: Observable<Job>;
1925
+ jobFailed$: Observable<Job>;
1926
+ }
1927
+ declare function createJobQueueVM(client: SemiontApiClient): JobQueueVM;
1928
+
1929
+ type Annotation = components['schemas']['Annotation'];
1930
+ interface AnnotationGroups {
1931
+ highlights: Annotation[];
1932
+ comments: Annotation[];
1933
+ assessments: Annotation[];
1934
+ references: Annotation[];
1935
+ tags: Annotation[];
1936
+ }
1937
+ type StoredEventResponse = components['schemas']['StoredEventResponse'];
1938
+ interface WizardState {
1939
+ open: boolean;
1940
+ annotationId: string | null;
1941
+ resourceId: string | null;
1942
+ defaultTitle: string;
1943
+ entityTypes: string[];
1944
+ }
1945
+ interface ResourceViewerPageVM extends ViewModel {
1946
+ beckon: BeckonVM;
1947
+ browse: ShellVM;
1948
+ mark: MarkVM;
1949
+ gather: GatherVM;
1950
+ yield: YieldVM;
1951
+ annotations$: Observable<Annotation[]>;
1952
+ annotationGroups$: Observable<AnnotationGroups>;
1953
+ entityTypes$: Observable<string[]>;
1954
+ events$: Observable<StoredEventResponse[]>;
1955
+ referencedBy$: Observable<ReferencedByEntry[]>;
1956
+ content$: Observable<string>;
1957
+ contentLoading$: Observable<boolean>;
1958
+ mediaToken$: Observable<string | null>;
1959
+ wizard$: Observable<WizardState>;
1960
+ closeWizard(): void;
1961
+ }
1962
+ declare function createResourceViewerPageVM(client: SemiontApiClient, resourceId: ResourceId, locale: string, browse: ShellVM, options?: {
1963
+ mediaType?: string;
1964
+ }): ResourceViewerPageVM;
1965
+
1966
+ type ResourceDescriptor = components['schemas']['ResourceDescriptor'];
1967
+ type ComposeMode = 'new' | 'clone' | 'reference';
1968
+ interface ComposeParams {
1969
+ mode?: string | undefined;
1970
+ token?: string | undefined;
1971
+ annotationUri?: string | undefined;
1972
+ sourceDocumentId?: string | undefined;
1973
+ name?: string | undefined;
1974
+ entityTypes?: string | undefined;
1975
+ storedContext?: string | undefined;
1976
+ }
1977
+ interface CloneData {
1978
+ sourceResource: ResourceDescriptor;
1979
+ sourceContent: string;
1980
+ }
1981
+ interface ReferenceData {
1982
+ annotationUri: string;
1983
+ sourceDocumentId: string;
1984
+ name: string;
1985
+ entityTypes: string[];
1986
+ }
1987
+ interface SaveResourceParams {
1988
+ mode: ComposeMode;
1989
+ name: string;
1990
+ storageUri: string;
1991
+ content?: string;
1992
+ file?: File;
1993
+ format?: string;
1994
+ charset?: string;
1995
+ entityTypes?: string[];
1996
+ language: string;
1997
+ archiveOriginal?: boolean;
1998
+ annotationUri?: string;
1999
+ sourceDocumentId?: string;
2000
+ }
2001
+ interface ComposePageVM extends ViewModel {
2002
+ browse: ShellVM;
2003
+ mode$: Observable<ComposeMode>;
2004
+ loading$: Observable<boolean>;
2005
+ cloneData$: Observable<CloneData | null>;
2006
+ referenceData$: Observable<ReferenceData | null>;
2007
+ gatheredContext$: Observable<GatheredContext | null>;
2008
+ entityTypes$: Observable<string[]>;
2009
+ save(params: SaveResourceParams): Promise<string>;
1287
2010
  }
2011
+ declare function createComposePageVM(client: SemiontApiClient, browse: ShellVM, params: ComposeParams, auth?: AccessToken): ComposePageVM;
1288
2012
 
1289
2013
  /**
1290
2014
  * MIME type utilities for Semiont
@@ -1324,4 +2048,4 @@ declare function isPdfMimeType(mimeType: string): boolean;
1324
2048
  type MimeCategory = 'text' | 'image' | 'unsupported';
1325
2049
  declare function getMimeCategory(mimeType: string): MimeCategory;
1326
2050
 
1327
- export { APIError, AdminNamespace, type AnnotationHistoryResponse, AuthNamespace, BeckonNamespace, BindNamespace, BrowseNamespace, type CreateAnnotationInput, type CreateFromTokenOptions, type CreateResourceInput, type GatherAnnotationProgress, GatherNamespace, type GenerationOptions, JobNamespace, type MarkAssistOptions, type MarkAssistProgress, MarkNamespace, MatchNamespace, type MatchSearchProgress, type MimeCategory, type ReferenceDetectionProgress, type ReferencedByEntry, type RequestContent$1 as RequestContent, type RequestOptions, type ResponseContent$1 as ResponseContent, SSEClient, type SSEClientConfig, type SSEStream, type SSEStreamConnected, SSE_STREAM_CONNECTED, SemiontApiClient, type SemiontApiClientConfig, type TokenRefresher, type User, YieldNamespace, type YieldProgress, getExtensionForMimeType, getMimeCategory, isImageMimeType, isPdfMimeType, isTextMimeType };
2051
+ export { APIError, type ActiveJob, type ActorVM, type ActorVMOptions, AdminNamespace, type AdminSecurityVM, type AdminUsersVM, type AnnotationGroups, type AnnotationHistoryResponse, AuthNamespace, BeckonNamespace, type BeckonVM, BindNamespace, BrowseNamespace, type BusEvent, BusRequestError, COMMON_PANELS, type Cache, type CloneData, type ComposeMode, type ComposePageVM, type ComposeParams, type ConnectionState, type CreateAnnotationInput, type CreateFromTokenOptions, type CreateResourceInput, DEGRADED_THRESHOLD_MS, type DiscoverVM, type EntityTagsVM, type ExchangeVM, FrontendSessionSignals, type GatherAnnotationProgress, GatherNamespace, type GatherVM, type GenerateDocumentOptions, type GenerationOptions, type GetBrowserOptions, HOVER_DELAY_MS, type HoverHandlers, type ImportPreview, InMemorySessionStorage, type Job, type JobAssignment, type JobClaimAdapter, type JobClaimAdapterOptions, JobNamespace, type JobQueueVM, type KbSessionStatus, type KnowledgeBase, type MarkAssistOptions, type MarkAssistProgress, MarkNamespace, type MarkVM, MatchNamespace, type MatchSearchProgress, type MatchVM, type MimeCategory, type NewKnowledgeBase, type OpenResource, type PendingAnnotation, RESOURCE_PANELS, type ReferenceData, type ReferencedByEntry, type RequestContent$1 as RequestContent, type RequestOptions, type ResourceLoaderVM, type ResourceViewerPageVM, type ResponseContent$1 as ResponseContent, type SaveResourceParams, type SearchPipeline, type SearchPipelineOptions, type SearchState, SemiontApiClient, type SemiontApiClientConfig, SemiontBrowser, type SemiontBrowserConfig, SemiontError, type SemiontErrorCode, SemiontSession, type SemiontSessionConfig, type SessionStorage, type SessionVM, type ShellVM, type ShellVMOptions, type SmelterActorVM, type SmelterActorVMOptions, type SmelterEvent, type StoredSession, type TokenRefresher, type ToolbarPanelType, type User, type UserInfo, type ViewModel, type WelcomeVM, type WizardState, YieldNamespace, type YieldVM, busRequest, createActorVM, createAdminSecurityVM, createAdminUsersVM, createBeckonVM, createCache, createComposePageVM, createDiscoverVM, createDisposer, createEntityTagsVM, createExchangeVM, createGatherVM, createHoverHandlers, createJobClaimAdapter, createJobQueueVM, createMarkVM, createMatchVM, createResourceLoaderVM, createResourceViewerPageVM, createSearchPipeline, createSessionVM, createShellVM, createSmelterActorVM, createWelcomeVM, createYieldVM, defaultProtocol, getBrowser, getExtensionForMimeType, getMimeCategory, isImageMimeType, isPdfMimeType, isTextMimeType, isValidHostname, kbBackendUrl, notifyPermissionDenied, notifySessionExpired, setStoredSession };