@semiont/sdk 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.
@@ -0,0 +1,1688 @@
1
+ import * as rxjs from 'rxjs';
2
+ import { Observable, BehaviorSubject, Subject } from 'rxjs';
3
+ import * as _semiont_core from '@semiont/core';
4
+ import { components, UserDID, paths, ResourceId, AnnotationId, BodyOperation, EventMap, ResourceDescriptor, Annotation, GraphConnection, Motivation, GatheredContext, JobId, ITransport, EventBus, IContentTransport, BaseUrl, AccessToken, ConnectionState, Selector } from '@semiont/core';
5
+ export { ConnectionState, Logger } from '@semiont/core';
6
+ import { ActorVM } from '@semiont/api-client';
7
+ export { APIError, ActorVM, ActorVMOptions, BusEvent, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, HttpTransportConfig, TokenRefresher, createActorVM } from '@semiont/api-client';
8
+
9
+ /**
10
+ * Verb Namespace Interfaces
11
+ *
12
+ * These interfaces define the public API of the Semiont api-client,
13
+ * organized by the 7 domain flows (Browse, Mark, Bind, Gather, Match,
14
+ * Yield, Beckon) plus infrastructure namespaces (Job, Auth, Admin).
15
+ *
16
+ * Each namespace maps 1:1 to a flow. Each flow maps to a clear actor
17
+ * on the backend. The frontend calls `client.mark.annotation()` and the
18
+ * proxy handles HTTP, auth, SSE, and caching internally.
19
+ *
20
+ * Return type conventions:
21
+ * - Browse live queries → Observable (bus gateway driven, cached)
22
+ * - Browse one-shot reads → Promise (fetch once, no cache)
23
+ * - Commands (mark, bind, yield.resource) → Promise (fire-and-forget)
24
+ * - Long-running ops (gather, match, yield.fromAnnotation, mark.assist) → Observable (progress + result)
25
+ * - Ephemeral signals (beckon) → void
26
+ */
27
+
28
+ type StoredEventResponse$2 = components['schemas']['StoredEventResponse'];
29
+ type GatherProgress = components['schemas']['GatherProgress'];
30
+ type MatchSearchResult = components['schemas']['MatchSearchResult'];
31
+ type JobProgress$2 = components['schemas']['JobProgress'];
32
+ type GatherAnnotationComplete = components['schemas']['GatherAnnotationComplete'];
33
+ type JobStatusResponse$1 = components['schemas']['JobStatusResponse'];
34
+ type AuthResponse$1 = components['schemas']['AuthResponse'];
35
+ type TokenRefreshResponse$1 = components['schemas']['TokenRefreshResponse'];
36
+ type OAuthConfigResponse$1 = components['schemas']['OAuthConfigResponse'];
37
+ type AdminUserStatsResponse$1 = components['schemas']['AdminUserStatsResponse'];
38
+ type ResponseContent<T> = T extends {
39
+ responses: {
40
+ 200: {
41
+ content: {
42
+ 'application/json': infer R;
43
+ };
44
+ };
45
+ };
46
+ } ? R : T extends {
47
+ responses: {
48
+ 201: {
49
+ content: {
50
+ 'application/json': infer R;
51
+ };
52
+ };
53
+ };
54
+ } ? R : T extends {
55
+ responses: {
56
+ 202: {
57
+ content: {
58
+ 'application/json': infer R;
59
+ };
60
+ };
61
+ };
62
+ } ? R : never;
63
+ type RequestContent<T> = T extends {
64
+ requestBody?: {
65
+ content: {
66
+ 'application/json': infer R;
67
+ };
68
+ };
69
+ } ? R : never;
70
+ /** Input for creating an annotation via mark.annotation() */
71
+ type CreateAnnotationInput = components['schemas']['CreateAnnotationRequest'];
72
+ /** Input for creating a resource via yield.resource() */
73
+ interface CreateResourceInput {
74
+ name: string;
75
+ file: File | Buffer;
76
+ format: string;
77
+ entityTypes?: string[];
78
+ language?: string;
79
+ creationMethod?: string;
80
+ sourceAnnotationId?: string;
81
+ sourceResourceId?: string;
82
+ storageUri: string;
83
+ /** Prompt that drove AI generation (for AI-generated resources). */
84
+ generationPrompt?: string;
85
+ /** Agent(s) that generated the content (for AI-generated resources). */
86
+ generator?: components['schemas']['Agent'] | components['schemas']['Agent'][];
87
+ isDraft?: boolean;
88
+ }
89
+ /** Options for yield.fromAnnotation() */
90
+ interface GenerationOptions {
91
+ title: string;
92
+ storageUri: string;
93
+ context: GatheredContext;
94
+ prompt?: string;
95
+ language?: string;
96
+ temperature?: number;
97
+ maxTokens?: number;
98
+ }
99
+ /** Options for mark.assist() */
100
+ interface MarkAssistOptions {
101
+ entityTypes?: string[];
102
+ includeDescriptiveReferences?: boolean;
103
+ instructions?: string;
104
+ density?: number;
105
+ tone?: string;
106
+ language?: string;
107
+ schemaId?: string;
108
+ categories?: string[];
109
+ }
110
+ /** Options for yield.createFromToken() */
111
+ type CreateFromTokenOptions = {
112
+ token: string;
113
+ name: string;
114
+ content: string;
115
+ archiveOriginal?: boolean;
116
+ };
117
+ /** Referenced-by entry from browse.referencedBy() */
118
+ type ReferencedByEntry = components['schemas']['GetReferencedByResponse']['referencedBy'][number];
119
+ /** Annotation history from browse.annotationHistory() */
120
+ type AnnotationHistoryResponse = components['schemas']['GetAnnotationHistoryResponse'];
121
+ /** User object from auth/admin responses */
122
+ type User = AuthResponse$1['user'];
123
+ /**
124
+ * Progress emitted by gather.annotation() Observable.
125
+ * Emits GatherProgress during assembly, then GatherAnnotationComplete on finish.
126
+ */
127
+ type GatherAnnotationProgress = GatherProgress | GatherAnnotationComplete;
128
+ /**
129
+ * Progress emitted by match.search() Observable.
130
+ * Emits the final MatchSearchResult (no intermediate progress events currently).
131
+ */
132
+ type MatchSearchProgress = MatchSearchResult;
133
+ /**
134
+ * Progress payload emitted by mark.assist() and yield.fromAnnotation()
135
+ * Observables. Each progress emission carries a JobProgress snapshot
136
+ * (unified job lifecycle).
137
+ */
138
+ type MarkAssistProgress = JobProgress$2;
139
+ /**
140
+ * Discriminated event yielded by the `mark.assist()` Observable. Progress
141
+ * events stream while the worker runs; the final value before the
142
+ * Observable completes is a `complete` event carrying the `JobCompleteCommand`
143
+ * payload (with `result`, `jobId`, `jobType`, etc.). The Observable errors
144
+ * on `job:fail`.
145
+ */
146
+ type MarkAssistEvent = {
147
+ kind: 'progress';
148
+ data: MarkAssistProgress;
149
+ } | {
150
+ kind: 'complete';
151
+ data: components['schemas']['JobCompleteCommand'];
152
+ };
153
+ /**
154
+ * Discriminated event yielded by the `yield.fromAnnotation()` Observable.
155
+ * Same shape and semantics as `MarkAssistEvent`.
156
+ */
157
+ type YieldGenerationEvent = {
158
+ kind: 'progress';
159
+ data: JobProgress$2;
160
+ } | {
161
+ kind: 'complete';
162
+ data: components['schemas']['JobCompleteCommand'];
163
+ };
164
+ /**
165
+ * Browse — reads from materialized views
166
+ *
167
+ * Live queries return Observables that emit initial state and re-emit
168
+ * on bus gateway updates. One-shot reads return Promises.
169
+ *
170
+ * Backend actor: Browser (context classes)
171
+ * Event prefix: browse:*
172
+ */
173
+ interface BrowseNamespace$1 {
174
+ resource(resourceId: ResourceId): Observable<ResourceDescriptor | undefined>;
175
+ resources(filters?: {
176
+ limit?: number;
177
+ archived?: boolean;
178
+ search?: string;
179
+ }): Observable<ResourceDescriptor[] | undefined>;
180
+ annotations(resourceId: ResourceId): Observable<Annotation[] | undefined>;
181
+ annotation(resourceId: ResourceId, annotationId: AnnotationId): Observable<Annotation | undefined>;
182
+ entityTypes(): Observable<string[] | undefined>;
183
+ referencedBy(resourceId: ResourceId): Observable<ReferencedByEntry[] | undefined>;
184
+ events(resourceId: ResourceId): Observable<StoredEventResponse$2[] | undefined>;
185
+ resourceContent(resourceId: ResourceId): Promise<string>;
186
+ resourceRepresentation(resourceId: ResourceId, options?: {
187
+ accept?: string;
188
+ }): Promise<{
189
+ data: ArrayBuffer;
190
+ contentType: string;
191
+ }>;
192
+ resourceRepresentationStream(resourceId: ResourceId, options?: {
193
+ accept?: string;
194
+ }): Promise<{
195
+ stream: ReadableStream<Uint8Array>;
196
+ contentType: string;
197
+ }>;
198
+ resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse$2[]>;
199
+ annotationHistory(resourceId: ResourceId, annotationId: AnnotationId): Promise<AnnotationHistoryResponse>;
200
+ connections(resourceId: ResourceId): Promise<GraphConnection[]>;
201
+ backlinks(resourceId: ResourceId): Promise<Annotation[]>;
202
+ resourcesByName(query: string, limit?: number): Promise<ResourceDescriptor[]>;
203
+ files(dirPath?: string, sort?: 'name' | 'mtime' | 'annotationCount'): Promise<components['schemas']['BrowseFilesResponse']>;
204
+ click(annotationId: AnnotationId, motivation: Motivation): void;
205
+ navigateReference(resourceId: ResourceId): void;
206
+ }
207
+ /**
208
+ * Mark — annotation CRUD, entity types, AI assist
209
+ *
210
+ * Commands return Promises that resolve on HTTP acceptance (202).
211
+ * Results appear on browse Observables via bus gateway.
212
+ * assist() returns an Observable for long-running progress.
213
+ *
214
+ * Backend actor: Stower
215
+ * Event prefix: mark:*
216
+ */
217
+ interface MarkNamespace$1 {
218
+ annotation(resourceId: ResourceId, input: CreateAnnotationInput): Promise<{
219
+ annotationId: string;
220
+ }>;
221
+ delete(resourceId: ResourceId, annotationId: AnnotationId): Promise<void>;
222
+ entityType(type: string): Promise<void>;
223
+ entityTypes(types: string[]): Promise<void>;
224
+ archive(resourceId: ResourceId): Promise<void>;
225
+ unarchive(resourceId: ResourceId): Promise<void>;
226
+ assist(resourceId: ResourceId, motivation: Motivation, options: MarkAssistOptions): Observable<MarkAssistEvent>;
227
+ request(selector: components['schemas']['MarkRequestedEvent']['selector'], motivation: Motivation): void;
228
+ /** Fire-and-forget variant of `assist` — mark-vm orchestrates the call and its progress Observable. */
229
+ requestAssist(motivation: Motivation, options: MarkAssistOptions, correlationId?: string): void;
230
+ /** Submit the currently pending annotation with its selector and optional body. */
231
+ submit(input: components['schemas']['MarkSubmitEvent']): void;
232
+ /** Cancel the currently pending annotation (if any). */
233
+ cancelPending(): void;
234
+ /** Dismiss the in-progress AI-assist widget. */
235
+ dismissProgress(): void;
236
+ changeSelection(motivation: Motivation | null): void;
237
+ changeClick(action: string): void;
238
+ changeShape(shape: string): void;
239
+ toggleMode(): void;
240
+ }
241
+ /**
242
+ * Bind — reference linking
243
+ *
244
+ * The simplest namespace. One method. The result (updated annotation
245
+ * with resolved reference) arrives on browse.annotations() via the
246
+ * enriched mark:body-updated event.
247
+ *
248
+ * Backend actor: Stower (via mark:update-body)
249
+ * Event prefix: mark:body-updated (shares mark event pipeline)
250
+ */
251
+ interface BindNamespace$1 {
252
+ body(resourceId: ResourceId, annotationId: AnnotationId, operations: BodyOperation[]): Promise<void>;
253
+ /** UI signal: a reference-binding flow is requested for an annotation. */
254
+ initiate(input: EventMap['bind:initiate']): void;
255
+ }
256
+ /**
257
+ * Gather — context assembly
258
+ *
259
+ * Long-running (LLM calls + graph traversal). Returns Observables
260
+ * that emit progress then the gathered context.
261
+ *
262
+ * Backend actor: Gatherer
263
+ * Event prefix: gather:*
264
+ */
265
+ interface GatherNamespace$1 {
266
+ annotation(annotationId: AnnotationId, resourceId: ResourceId, options?: {
267
+ contextWindow?: number;
268
+ }): Observable<GatherAnnotationProgress>;
269
+ resource(resourceId: ResourceId, options?: {
270
+ contextWindow?: number;
271
+ }): Observable<GatherAnnotationProgress>;
272
+ }
273
+ /**
274
+ * Match — search and ranking
275
+ *
276
+ * Long-running (semantic search, optional LLM scoring). Returns
277
+ * Observable with progress then results.
278
+ *
279
+ * Backend actor: Matcher
280
+ * Event prefix: match:*
281
+ */
282
+ interface MatchNamespace$1 {
283
+ search(resourceId: ResourceId, referenceId: string, context: GatheredContext, options?: {
284
+ limit?: number;
285
+ useSemanticScoring?: boolean;
286
+ }): Observable<MatchSearchProgress>;
287
+ /** Fire-and-forget variant: match-vm orchestrates the call and its result Observable. */
288
+ requestSearch(input: components['schemas']['MatchSearchRequest']): void;
289
+ }
290
+ /**
291
+ * Yield — resource creation
292
+ *
293
+ * resource() is synchronous file upload (Promise).
294
+ * fromAnnotation() is long-running LLM generation (Observable).
295
+ *
296
+ * Backend actor: Stower + generation worker
297
+ * Event prefix: yield:*
298
+ */
299
+ interface YieldNamespace$1 {
300
+ resource(data: CreateResourceInput): Promise<{
301
+ resourceId: string;
302
+ }>;
303
+ fromAnnotation(resourceId: ResourceId, annotationId: AnnotationId, options: GenerationOptions): Observable<YieldGenerationEvent>;
304
+ cloneToken(resourceId: ResourceId): Promise<{
305
+ token: string;
306
+ expiresAt: string;
307
+ }>;
308
+ fromToken(token: string): Promise<ResourceDescriptor>;
309
+ createFromToken(options: CreateFromTokenOptions): Promise<{
310
+ resourceId: string;
311
+ }>;
312
+ /** UI signal: user invoked the clone action from the resource-info panel. */
313
+ clone(): void;
314
+ }
315
+ /**
316
+ * Beckon — attention coordination
317
+ *
318
+ * Fire-and-forget. Ephemeral presence signal delivered via the
319
+ * attention-stream to other participants.
320
+ *
321
+ * Backend actor: (frontend relay via attention-stream)
322
+ * Event prefix: beckon:*
323
+ */
324
+ interface BeckonNamespace$1 {
325
+ attention(annotationId: AnnotationId, resourceId: ResourceId): void;
326
+ hover(annotationId: AnnotationId | null): void;
327
+ sparkle(annotationId: AnnotationId): void;
328
+ }
329
+ /**
330
+ * Job — worker lifecycle
331
+ */
332
+ interface JobNamespace$1 {
333
+ /** Live stream of `job:queued` events from the bus. */
334
+ readonly queued$: Observable<EventMap['job:queued']>;
335
+ /** Live stream of `job:report-progress` events from the bus. */
336
+ readonly progress$: Observable<EventMap['job:report-progress']>;
337
+ /** Live stream of `job:complete` events from the bus. */
338
+ readonly complete$: Observable<EventMap['job:complete']>;
339
+ /** Live stream of `job:fail` events from the bus. */
340
+ readonly fail$: Observable<EventMap['job:fail']>;
341
+ status(jobId: JobId): Promise<JobStatusResponse$1>;
342
+ pollUntilComplete(jobId: JobId, options?: {
343
+ interval?: number;
344
+ timeout?: number;
345
+ onProgress?: (status: JobStatusResponse$1) => void;
346
+ }): Promise<JobStatusResponse$1>;
347
+ cancel(jobId: JobId, type: string): Promise<void>;
348
+ /** UI signal: cancel all active jobs of a given type (e.g. "annotation"). */
349
+ cancelRequest(jobType: 'annotation' | 'generation'): void;
350
+ }
351
+ /**
352
+ * Auth — authentication
353
+ */
354
+ interface AuthNamespace$1 {
355
+ password(email: string, password: string): Promise<AuthResponse$1>;
356
+ google(credential: string): Promise<AuthResponse$1>;
357
+ refresh(token: string): Promise<TokenRefreshResponse$1>;
358
+ logout(): Promise<void>;
359
+ me(): Promise<User>;
360
+ acceptTerms(): Promise<void>;
361
+ mcpToken(): Promise<{
362
+ token: string;
363
+ }>;
364
+ mediaToken(resourceId: ResourceId): Promise<{
365
+ token: string;
366
+ }>;
367
+ }
368
+ /**
369
+ * Admin — administration
370
+ */
371
+ interface AdminNamespace$1 {
372
+ users(): Promise<User[]>;
373
+ userStats(): Promise<AdminUserStatsResponse$1>;
374
+ updateUser(userId: UserDID, data: RequestContent<paths['/api/admin/users/{id}']['patch']>): Promise<User>;
375
+ oauthConfig(): Promise<OAuthConfigResponse$1>;
376
+ healthCheck(): Promise<ResponseContent<paths['/api/health']['get']>>;
377
+ status(): Promise<ResponseContent<paths['/api/status']['get']>>;
378
+ backup(): Promise<Response>;
379
+ restore(file: File, onProgress?: (event: {
380
+ phase: string;
381
+ message?: string;
382
+ result?: Record<string, unknown>;
383
+ }) => void): Promise<{
384
+ phase: string;
385
+ message?: string;
386
+ result?: Record<string, unknown>;
387
+ }>;
388
+ exportKnowledgeBase(params?: {
389
+ includeArchived?: boolean;
390
+ }): Promise<Response>;
391
+ importKnowledgeBase(file: File, onProgress?: (event: {
392
+ phase: string;
393
+ message?: string;
394
+ result?: Record<string, unknown>;
395
+ }) => void): Promise<{
396
+ phase: string;
397
+ message?: string;
398
+ result?: Record<string, unknown>;
399
+ }>;
400
+ }
401
+
402
+ type StoredEventResponse$1 = components['schemas']['StoredEventResponse'];
403
+ type ResourceListFilters = {
404
+ limit?: number;
405
+ archived?: boolean;
406
+ search?: string;
407
+ };
408
+ declare class BrowseNamespace implements BrowseNamespace$1 {
409
+ private readonly transport;
410
+ private readonly bus;
411
+ private readonly content;
412
+ private readonly resourceCache;
413
+ private readonly resourceListCache;
414
+ private readonly annotationListCache;
415
+ /**
416
+ * Annotation-detail cache keyed by `annotationId` only — the resourceId
417
+ * is a routing hint for the backend fetch, not an identity component.
418
+ * We track the most recent resourceId per annotationId in a side-map
419
+ * so `mark:delete-ok` (which carries only `annotationId`) can reach
420
+ * the right cache entry. Aligns with the pre-refactor semantics.
421
+ */
422
+ private readonly annotationDetailCache;
423
+ private readonly annotationResources;
424
+ private readonly entityTypesCache;
425
+ private readonly referencedByCache;
426
+ private readonly resourceEventsCache;
427
+ /** Filter-blob memory so `invalidateResourceLists` can replay per-key. */
428
+ private readonly resourceListFilters;
429
+ /**
430
+ * Per-key memo for `annotations()` observables. The cache stores the
431
+ * full `AnnotationsListResponse`; the public shape is just the inner
432
+ * `Annotation[]`. Without this memo, every call to `annotations(rId)`
433
+ * would produce a fresh `.pipe(map(...))` observable, violating B4
434
+ * (per-key observable stability). Consumers that compare observable
435
+ * identity — React hooks depending on the observable reference,
436
+ * `distinctUntilChanged` at a higher level — would misbehave.
437
+ */
438
+ private readonly annotationListObs;
439
+ constructor(transport: ITransport, bus: EventBus, content: IContentTransport);
440
+ resource(resourceId: ResourceId): Observable<ResourceDescriptor | undefined>;
441
+ resources(filters?: ResourceListFilters): Observable<ResourceDescriptor[] | undefined>;
442
+ annotations(resourceId: ResourceId): Observable<Annotation[] | undefined>;
443
+ annotation(resourceId: ResourceId, annotationId: AnnotationId): Observable<Annotation | undefined>;
444
+ entityTypes(): Observable<string[] | undefined>;
445
+ referencedBy(resourceId: ResourceId): Observable<ReferencedByEntry[] | undefined>;
446
+ events(resourceId: ResourceId): Observable<StoredEventResponse$1[] | undefined>;
447
+ resourceContent(resourceId: ResourceId): Promise<string>;
448
+ resourceRepresentation(resourceId: ResourceId, options?: {
449
+ accept?: string;
450
+ }): Promise<{
451
+ data: ArrayBuffer;
452
+ contentType: string;
453
+ }>;
454
+ resourceRepresentationStream(resourceId: ResourceId, options?: {
455
+ accept?: string;
456
+ }): Promise<{
457
+ stream: ReadableStream<Uint8Array>;
458
+ contentType: string;
459
+ }>;
460
+ resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse$1[]>;
461
+ annotationHistory(resourceId: ResourceId, annotationId: AnnotationId): Promise<AnnotationHistoryResponse>;
462
+ connections(_resourceId: ResourceId): Promise<GraphConnection[]>;
463
+ backlinks(_resourceId: ResourceId): Promise<Annotation[]>;
464
+ resourcesByName(_query: string, _limit?: number): Promise<ResourceDescriptor[]>;
465
+ files(dirPath?: string, sort?: 'name' | 'mtime' | 'annotationCount'): Promise<components['schemas']['BrowseFilesResponse']>;
466
+ click(annotationId: AnnotationId, motivation: Motivation): void;
467
+ navigateReference(resourceId: ResourceId): void;
468
+ invalidateAnnotationList(resourceId: ResourceId): void;
469
+ removeAnnotationDetail(annotationId: AnnotationId): void;
470
+ invalidateResourceDetail(id: ResourceId): void;
471
+ invalidateResourceLists(): void;
472
+ invalidateEntityTypes(): void;
473
+ invalidateReferencedBy(resourceId: ResourceId): void;
474
+ invalidateResourceEvents(resourceId: ResourceId): void;
475
+ updateAnnotationInPlace(resourceId: ResourceId, annotation: Annotation): void;
476
+ /**
477
+ * Typed shorthand for `eventBus.get(channel).subscribe(handler)`.
478
+ * Preserves per-channel payload typing so handlers read
479
+ * `EventMap[K]` without any casts.
480
+ */
481
+ private on;
482
+ /**
483
+ * Handler shared by `mark:entity-tag-added` and `mark:entity-tag-removed`.
484
+ * Both events carry the same effect: the annotation list, the
485
+ * resource descriptor, and the event log for that resource all may
486
+ * now reflect different entity tagging, so invalidate all three.
487
+ */
488
+ private onEntityTagChanged;
489
+ /**
490
+ * Handler shared by `mark:archived` and `mark:unarchived`. Both
491
+ * change a resource's archived flag, which is stored on the resource
492
+ * descriptor and affects the resource-list filter.
493
+ */
494
+ private onArchiveToggled;
495
+ /**
496
+ * Handler shared by `yield:create-ok` and `yield:update-ok`. Both
497
+ * report a resource mutation with the resourceId as a string (not
498
+ * yet branded), so we brand and apply the same effect as
499
+ * `onArchiveToggled`.
500
+ */
501
+ private onYieldResourceMutated;
502
+ private subscribeToEvents;
503
+ }
504
+
505
+ declare class MarkNamespace implements MarkNamespace$1 {
506
+ private readonly transport;
507
+ private readonly bus;
508
+ constructor(transport: ITransport, bus: EventBus);
509
+ annotation(resourceId: ResourceId, input: CreateAnnotationInput): Promise<{
510
+ annotationId: string;
511
+ }>;
512
+ delete(resourceId: ResourceId, annotationId: AnnotationId): Promise<void>;
513
+ entityType(type: string): Promise<void>;
514
+ entityTypes(types: string[]): Promise<void>;
515
+ archive(resourceId: ResourceId): Promise<void>;
516
+ unarchive(resourceId: ResourceId): Promise<void>;
517
+ assist(resourceId: ResourceId, motivation: Motivation, options: MarkAssistOptions): Observable<MarkAssistEvent>;
518
+ request(selector: components['schemas']['MarkRequestedEvent']['selector'], motivation: Motivation): void;
519
+ requestAssist(motivation: Motivation, options: MarkAssistOptions, correlationId?: string): void;
520
+ submit(input: components['schemas']['MarkSubmitEvent']): void;
521
+ cancelPending(): void;
522
+ dismissProgress(): void;
523
+ changeSelection(motivation: Motivation | null): void;
524
+ changeClick(action: string): void;
525
+ changeShape(shape: string): void;
526
+ toggleMode(): void;
527
+ private dispatchAssist;
528
+ }
529
+
530
+ declare class BindNamespace implements BindNamespace$1 {
531
+ private readonly transport;
532
+ private readonly bus;
533
+ constructor(transport: ITransport, bus: EventBus);
534
+ body(resourceId: ResourceId, annotationId: AnnotationId, operations: BodyOperation[]): Promise<void>;
535
+ initiate(input: EventMap['bind:initiate']): void;
536
+ }
537
+
538
+ declare class GatherNamespace implements GatherNamespace$1 {
539
+ private readonly transport;
540
+ private readonly bus;
541
+ constructor(transport: ITransport, bus: EventBus);
542
+ annotation(annotationId: AnnotationId, resourceId: ResourceId, options?: {
543
+ contextWindow?: number;
544
+ }): Observable<GatherAnnotationProgress>;
545
+ resource(_resourceId: ResourceId, _options?: {
546
+ contextWindow?: number;
547
+ }): Observable<GatherAnnotationProgress>;
548
+ }
549
+
550
+ declare class MatchNamespace implements MatchNamespace$1 {
551
+ private readonly transport;
552
+ private readonly bus;
553
+ constructor(transport: ITransport, bus: EventBus);
554
+ requestSearch(input: components['schemas']['MatchSearchRequest']): void;
555
+ search(resourceId: ResourceId, referenceId: string, context: GatheredContext, options?: {
556
+ limit?: number;
557
+ useSemanticScoring?: boolean;
558
+ }): Observable<MatchSearchProgress>;
559
+ }
560
+
561
+ declare class YieldNamespace implements YieldNamespace$1 {
562
+ private readonly transport;
563
+ private readonly bus;
564
+ private readonly content;
565
+ constructor(transport: ITransport, bus: EventBus, content: IContentTransport);
566
+ resource(data: CreateResourceInput): Promise<{
567
+ resourceId: string;
568
+ }>;
569
+ fromAnnotation(resourceId: ResourceId, annotationId: AnnotationId, options: GenerationOptions): Observable<YieldGenerationEvent>;
570
+ cloneToken(resourceId: ResourceId): Promise<{
571
+ token: string;
572
+ expiresAt: string;
573
+ }>;
574
+ fromToken(token: string): Promise<ResourceDescriptor>;
575
+ createFromToken(options: CreateFromTokenOptions): Promise<{
576
+ resourceId: string;
577
+ }>;
578
+ clone(): void;
579
+ }
580
+
581
+ declare class BeckonNamespace implements BeckonNamespace$1 {
582
+ private readonly transport;
583
+ private readonly bus;
584
+ constructor(transport: ITransport, bus: EventBus);
585
+ attention(annotationId: AnnotationId, resourceId: ResourceId): void;
586
+ hover(annotationId: AnnotationId | null): void;
587
+ sparkle(annotationId: AnnotationId): void;
588
+ }
589
+
590
+ type JobStatusResponse = components['schemas']['JobStatusResponse'];
591
+ declare class JobNamespace implements JobNamespace$1 {
592
+ private readonly transport;
593
+ private readonly bus;
594
+ constructor(transport: ITransport, bus: EventBus);
595
+ /**
596
+ * Live stream of `job:queued` events. Surfaces a typed view onto the
597
+ * underlying bus channel for consumers (CLIs, MCP handlers, widgets)
598
+ * that orchestrate jobs and need to react to lifecycle transitions.
599
+ */
600
+ get queued$(): Observable<EventMap['job:queued']>;
601
+ /** Live stream of `job:report-progress` events. */
602
+ get progress$(): Observable<EventMap['job:report-progress']>;
603
+ /** Live stream of `job:complete` events. */
604
+ get complete$(): Observable<EventMap['job:complete']>;
605
+ /** Live stream of `job:fail` events. */
606
+ get fail$(): Observable<EventMap['job:fail']>;
607
+ status(jobId: JobId): Promise<JobStatusResponse>;
608
+ pollUntilComplete(jobId: JobId, options?: {
609
+ interval?: number;
610
+ timeout?: number;
611
+ onProgress?: (status: JobStatusResponse) => void;
612
+ }): Promise<JobStatusResponse>;
613
+ cancel(_jobId: JobId, type: string): Promise<void>;
614
+ cancelRequest(jobType: 'annotation' | 'generation'): void;
615
+ }
616
+
617
+ /**
618
+ * AuthNamespace — authentication. Pure wire, no bus.
619
+ */
620
+
621
+ type AuthResponse = components['schemas']['AuthResponse'];
622
+ type TokenRefreshResponse = components['schemas']['TokenRefreshResponse'];
623
+ declare class AuthNamespace implements AuthNamespace$1 {
624
+ private readonly transport;
625
+ constructor(transport: ITransport);
626
+ password(emailStr: string, passwordStr: string): Promise<AuthResponse>;
627
+ google(credential: string): Promise<AuthResponse>;
628
+ refresh(token: string): Promise<TokenRefreshResponse>;
629
+ logout(): Promise<void>;
630
+ me(): Promise<User>;
631
+ acceptTerms(): Promise<void>;
632
+ mcpToken(): Promise<{
633
+ token: string;
634
+ }>;
635
+ mediaToken(resourceId: ResourceId): Promise<{
636
+ token: string;
637
+ }>;
638
+ }
639
+
640
+ /**
641
+ * AdminNamespace — administration. Pure wire, no bus.
642
+ */
643
+
644
+ type AdminUserStatsResponse = components['schemas']['AdminUserStatsResponse'];
645
+ type OAuthConfigResponse = components['schemas']['OAuthConfigResponse'];
646
+ declare class AdminNamespace implements AdminNamespace$1 {
647
+ private readonly transport;
648
+ constructor(transport: ITransport);
649
+ users(): Promise<User[]>;
650
+ userStats(): Promise<AdminUserStatsResponse>;
651
+ updateUser(userId: UserDID, data: RequestContent<paths['/api/admin/users/{id}']['patch']>): Promise<User>;
652
+ oauthConfig(): Promise<OAuthConfigResponse>;
653
+ healthCheck(): Promise<ResponseContent<paths['/api/health']['get']>>;
654
+ status(): Promise<ResponseContent<paths['/api/status']['get']>>;
655
+ backup(): Promise<Response>;
656
+ restore(file: File, onProgress?: (event: {
657
+ phase: string;
658
+ message?: string;
659
+ result?: Record<string, unknown>;
660
+ }) => void): Promise<{
661
+ phase: string;
662
+ message?: string;
663
+ result?: Record<string, unknown>;
664
+ }>;
665
+ exportKnowledgeBase(params?: {
666
+ includeArchived?: boolean;
667
+ }): Promise<Response>;
668
+ importKnowledgeBase(file: File, onProgress?: (event: {
669
+ phase: string;
670
+ message?: string;
671
+ result?: Record<string, unknown>;
672
+ }) => void): Promise<{
673
+ phase: string;
674
+ message?: string;
675
+ result?: Record<string, unknown>;
676
+ }>;
677
+ }
678
+
679
+ declare class SemiontClient {
680
+ /**
681
+ * The wire-facing transport. Owns bus actor, HTTP, auth, admin, exchange,
682
+ * system. Exposed for advanced consumers (workers, custom job adapters)
683
+ * that need raw `transport.emit(channel, payload, scope)` access. Ordinary
684
+ * consumers go through typed namespace methods.
685
+ */
686
+ readonly transport: ITransport;
687
+ /** Binary I/O transport. */
688
+ private readonly content;
689
+ /**
690
+ * Per-client local EventBus. Wire events flow in via the transport
691
+ * bridge. Read-only public so `SemiontSession.subscribe(channel, …)`
692
+ * can wire arbitrary-channel subscriptions; everything else uses
693
+ * typed namespace methods.
694
+ */
695
+ readonly bus: EventBus;
696
+ readonly baseUrl: BaseUrl;
697
+ readonly browse: BrowseNamespace;
698
+ readonly mark: MarkNamespace;
699
+ readonly bind: BindNamespace;
700
+ readonly gather: GatherNamespace;
701
+ readonly match: MatchNamespace;
702
+ readonly yield: YieldNamespace;
703
+ readonly beckon: BeckonNamespace;
704
+ readonly job: JobNamespace;
705
+ readonly auth: AuthNamespace;
706
+ readonly admin: AdminNamespace;
707
+ /**
708
+ * The client *owns* its bus. The constructor creates a fresh `EventBus`
709
+ * and hands it to the transport via `transport.bridgeInto(this.bus)`.
710
+ * The reference flows client → transport, never the other way:
711
+ * the transport stores the reference and publishes the events it
712
+ * receives onto that bus. `HttpTransport` does so for every channel
713
+ * delivered on its SSE wire; in-process transports adapt their
714
+ * internal source.
715
+ *
716
+ * Callers do not pass a bus in. If they need to interact with the bus
717
+ * (e.g. for tests or to subscribe to arbitrary channels), they read it
718
+ * back via `client.bus`.
719
+ */
720
+ constructor(transport: ITransport, content: IContentTransport);
721
+ /** Transport-level connection state. HTTP reflects SSE health; local is always 'connected'. */
722
+ get state$(): rxjs.Observable<_semiont_core.ConnectionState>;
723
+ subscribeToResource(resourceId: ResourceId): () => void;
724
+ dispose(): void;
725
+ }
726
+
727
+ declare class BusRequestError extends Error {
728
+ constructor(message: string);
729
+ }
730
+ /**
731
+ * Subset of ITransport that `busRequest` needs: a way to send a command and
732
+ * a way to observe channels. Generic enough that an in-process transport
733
+ * can satisfy it without round-tripping through HTTP.
734
+ */
735
+ interface BusRequestPrimitive {
736
+ emit<K extends keyof EventMap>(channel: K, payload: EventMap[K]): Promise<void>;
737
+ stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]>;
738
+ }
739
+ declare function busRequest<TResult>(bus: BusRequestPrimitive, emitChannel: string, payload: Record<string, unknown>, resultChannel: string, failureChannel: string, timeoutMs?: number): Promise<TResult>;
740
+
741
+ /**
742
+ * RxJS-native read-through cache primitive.
743
+ *
744
+ * Behavioral contract: packages/api-client/docs/CACHE-SEMANTICS.md (B1–B13).
745
+ *
746
+ * Framework-agnostic: no React, no dependency on any namespace. Used by
747
+ * `BrowseNamespace` to back its per-key stores, but equally usable from
748
+ * CLI, MCP, or worker code.
749
+ *
750
+ * Shape:
751
+ * - `observe(key)`: returns an Observable<V | undefined> that triggers
752
+ * a fetch on first subscription for a missing key, dedup-joins any
753
+ * concurrent fetch, and emits the stored value thereafter.
754
+ * - `invalidate(key)`: stale-while-revalidate — keeps the current
755
+ * value visible to observers, clears the in-flight guard, starts a
756
+ * fresh fetch. If the previous fetch was orphaned (SSE torn down,
757
+ * response lost), this is how the cache recovers.
758
+ * - `remove(key)`: drops the cache entry entirely. Used for entity
759
+ * deletions (B13a). No refetch.
760
+ * - `set(key, value)`: writes through without a fetch. Used when a
761
+ * bus event carries the new value inline (B13b).
762
+ * - `invalidateAll()`: per-key SWR refetch of every currently-cached
763
+ * entry. Used by gap-detection paths.
764
+ * - `dispose()`: completes the store so observers unsubscribe.
765
+ *
766
+ * What's deliberately out:
767
+ * - No subscriber ref-counting / GC of unobserved keys. The per-key
768
+ * observable memo grows with the set of observed keys for the cache's
769
+ * lifetime (B11). Acceptable given cache lifetime == client lifetime.
770
+ * - No TTL / cacheTime. Entries are evicted only by explicit remove.
771
+ * - No retry / backoff. A failing fetch leaves the cache unchanged
772
+ * (B6); the caller drives retry via invalidate.
773
+ */
774
+
775
+ interface Cache<K, V> {
776
+ /** Observable stream of the value at `key`. Triggers a fetch if not cached. */
777
+ observe(key: K): Observable<V | undefined>;
778
+ /** Synchronous snapshot of the current value, without triggering a fetch. */
779
+ get(key: K): V | undefined;
780
+ /** Iterator of currently-cached keys. For invalidateAll and diagnostics. */
781
+ keys(): K[];
782
+ /**
783
+ * Mark the entry stale and refetch. Keeps the previous value visible
784
+ * to observers during the refetch (stale-while-revalidate).
785
+ */
786
+ invalidate(key: K): void;
787
+ /** Drop the entry from the cache. No refetch. */
788
+ remove(key: K): void;
789
+ /** Write-through: set the value directly without a fetch. */
790
+ set(key: K, value: V): void;
791
+ /** Per-key SWR refetch of every currently-cached entry. */
792
+ invalidateAll(): void;
793
+ /** Release the underlying subject. Observers complete. */
794
+ dispose(): void;
795
+ }
796
+ declare function createCache<K, V>(fetchFn: (key: K) => Promise<V>): Cache<K, V>;
797
+
798
+ /**
799
+ * KnowledgeBase — a connection to a Semiont backend instance.
800
+ *
801
+ * Each KB has its own JWT, its own API base URL, and its own session.
802
+ * The user is "authenticated against KB X" — never globally authenticated.
803
+ */
804
+ interface KnowledgeBase {
805
+ id: string;
806
+ label: string;
807
+ host: string;
808
+ port: number;
809
+ protocol: 'http' | 'https';
810
+ email: string;
811
+ gitBranch?: string;
812
+ }
813
+ /**
814
+ * Input shape for adding a new KB. The id is generated by the provider.
815
+ */
816
+ type NewKnowledgeBase = Omit<KnowledgeBase, 'id'>;
817
+ /**
818
+ * Status of the locally-stored credential for a KB. Derived from the
819
+ * presence and validity of the JWT in session storage.
820
+ */
821
+ type KbSessionStatus = 'authenticated' | 'expired' | 'signed-out' | 'unreachable';
822
+
823
+ /**
824
+ * Session-level error surface. Emitted on `SemiontBrowser.error$` for
825
+ * failures that make the session itself unusable (auth failed, actor
826
+ * couldn't start, token refresh terminally exhausted). Per-request
827
+ * errors stay with the caller as normal Promise rejections.
828
+ */
829
+ type SemiontErrorCode = 'session.construct-failed' | 'session.auth-failed' | 'session.refresh-exhausted' | 'browser.sign-in-failed';
830
+ declare class SemiontError extends Error {
831
+ readonly code: SemiontErrorCode;
832
+ readonly kbId: string | null;
833
+ constructor(code: SemiontErrorCode, message: string, kbId?: string | null);
834
+ }
835
+
836
+ /**
837
+ * SessionStorage — environment-agnostic persistence adapter for the
838
+ * Semiont session layer. Decouples `SemiontSession` / `SemiontBrowser`
839
+ * from `localStorage` / `window` so the same classes can run in a
840
+ * browser, a CLI process, or tests without environment guards.
841
+ *
842
+ * Implementations shipped here:
843
+ * - `InMemorySessionStorage` — map-backed, for tests.
844
+ *
845
+ * Browser-backed (`WebBrowserStorage`) lives in `@semiont/react-ui`
846
+ * because it touches browser-only globals.
847
+ */
848
+ /** String key/value store with optional cross-context change subscription. */
849
+ interface SessionStorage {
850
+ /** Read a string value; null if absent. */
851
+ get(key: string): string | null;
852
+ /** Write a string value. */
853
+ set(key: string, value: string): void;
854
+ /** Remove a key. No-op if absent. */
855
+ delete(key: string): void;
856
+ /**
857
+ * Optional: subscribe to external changes (cross-tab, cross-process).
858
+ * Browser implements via the `storage` event; filesystem would use
859
+ * fs.watch; in-memory omits this method. Returns an unsubscribe
860
+ * callback. If omitted, cross-context sync simply isn't available
861
+ * in that environment — the session still works correctly within a
862
+ * single process.
863
+ */
864
+ subscribe?(handler: (key: string, newValue: string | null) => void): () => void;
865
+ }
866
+ /**
867
+ * Map-backed `SessionStorage`. Cross-context sync is not implemented;
868
+ * tests that need it can drive it manually.
869
+ */
870
+ declare class InMemorySessionStorage implements SessionStorage {
871
+ private readonly map;
872
+ get(key: string): string | null;
873
+ set(key: string, value: string): void;
874
+ delete(key: string): void;
875
+ }
876
+
877
+ /**
878
+ * SemiontSession — per-backend session lifetime object. Owns the
879
+ * SemiontClient, the access token BehaviorSubject, and optionally
880
+ * an authenticated user. One SemiontSession exists per active backend
881
+ * connection; lifetime is decoupled from React mount lifetime.
882
+ *
883
+ * Headless by design. Runs in browsers, CLIs, workers, and tests.
884
+ * UI-specific state (session-expired/permission-denied modals) lives
885
+ * in `FrontendSessionSignals`, which wraps a session — the session
886
+ * itself has no modal observables, no user-facing notifications.
887
+ *
888
+ * Auth is parameterized via callbacks passed at construction:
889
+ *
890
+ * - `refresh()` — invoked on 401 / proactive re-auth. Returns the
891
+ * new access token, or null on failure. The frontend passes a
892
+ * closure that runs the refresh-token flow; the worker passes
893
+ * one that exchanges the shared secret.
894
+ *
895
+ * - `validate(token)` — optional. If provided, the session calls
896
+ * it once at startup with the stored token to confirm it's
897
+ * still good and populate `user$`. Frontend passes `getMe`;
898
+ * worker omits this (service principals have no user record).
899
+ *
900
+ * - `onAuthFailed(message)` — optional. Invoked when refresh
901
+ * terminally fails (expired token, no recovery possible). The
902
+ * frontend wires this to `FrontendSessionSignals.notifySessionExpired`
903
+ * so the modal surfaces; headless consumers typically just log.
904
+ *
905
+ * Persistence goes through a `SessionStorage` adapter provided at
906
+ * construction — the session never touches `localStorage` or `window`
907
+ * directly.
908
+ */
909
+
910
+ type UserInfo = components['schemas']['UserResponse'];
911
+ interface SemiontSessionConfig {
912
+ kb: KnowledgeBase;
913
+ /** Persistence adapter. Reads/writes tokens via this. */
914
+ storage: SessionStorage;
915
+ /**
916
+ * Pre-built api client. The session does not construct it — caller
917
+ * builds the transport stack and passes the client in. This is the
918
+ * seam where consumers swap one `ITransport` implementation for
919
+ * another (HTTP, in-process, etc.).
920
+ */
921
+ client: SemiontClient;
922
+ /**
923
+ * Token observable shared with the transport. Caller must pass the
924
+ * SAME instance to both the transport (via `HttpTransport` config)
925
+ * and the session. The session writes refreshed tokens here; the
926
+ * transport reads from here.
927
+ */
928
+ token$: BehaviorSubject<AccessToken | null>;
929
+ /**
930
+ * Re-authenticate after expiry / 401. Returns a new access token
931
+ * (no "Bearer " prefix) on success, or null if recovery is
932
+ * impossible. Omit for transports where tokens don't apply.
933
+ */
934
+ refresh?: () => Promise<string | null>;
935
+ /**
936
+ * Validate the stored token at startup and populate `user$`. Omit
937
+ * for service-principal sessions (worker, CLI tools) where there
938
+ * is no user record to fetch.
939
+ */
940
+ validate?: (token: AccessToken) => Promise<UserInfo | null>;
941
+ /**
942
+ * Invoked when refresh terminally fails. Frontend consumers wire
943
+ * this to a UI signal that surfaces the session-expired modal.
944
+ */
945
+ onAuthFailed?: (message: string | null) => void;
946
+ /** Called for session-level failures (auth, refresh exhaustion). */
947
+ onError?: (err: SemiontError) => void;
948
+ }
949
+ declare class SemiontSession {
950
+ readonly kb: KnowledgeBase;
951
+ readonly client: SemiontClient;
952
+ readonly token$: BehaviorSubject<AccessToken | null>;
953
+ readonly user$: BehaviorSubject<UserInfo | null>;
954
+ readonly streamState$: Observable<ConnectionState>;
955
+ /** Resolves after the initial validation round-trip completes (success or failure). */
956
+ readonly ready: Promise<void>;
957
+ private readonly storage;
958
+ private readonly doRefresh?;
959
+ private readonly doValidate?;
960
+ private readonly onAuthFailed;
961
+ private readonly onError;
962
+ private refreshTimer;
963
+ private unsubscribeStorage;
964
+ private disposed;
965
+ constructor(config: SemiontSessionConfig);
966
+ /**
967
+ * Run the initial mount-time validation. If a stored access token is
968
+ * present and unexpired, call the configured `validate` with it to
969
+ * confirm it still works and populate `user$`. If expired, try
970
+ * refresh first. On 401 from validate, try refresh once. Surfaces
971
+ * auth-failed on terminal failure.
972
+ *
973
+ * When no `validate` callback is provided (service principals), this
974
+ * still runs through the refresh-if-expired step so the stored
975
+ * token is current — it just skips the user-validation round trip.
976
+ */
977
+ private validate;
978
+ /**
979
+ * Refresh the access token via the configured `refresh` callback.
980
+ * On success, pushes the new token into `token$` and schedules the
981
+ * next proactive refresh. On failure, clears persisted state and
982
+ * fires `onAuthFailed` — the frontend's wiring of that callback is
983
+ * what surfaces the session-expired modal.
984
+ */
985
+ refresh(): Promise<AccessToken | null>;
986
+ private scheduleProactiveRefresh;
987
+ private clearRefreshTimer;
988
+ /**
989
+ * Cross-context sync: another tab/process refreshed or signed out this
990
+ * KB. Mirror the change into our in-memory state.
991
+ */
992
+ private handleStorageChange;
993
+ get expiresAt(): Date | null;
994
+ /**
995
+ * Subscribe to a session-bus channel. The single sanctioned escape hatch
996
+ * for generic-channel subscription (the case `useEventSubscription` needs
997
+ * — channel name is a hook parameter, not known statically). All other
998
+ * consumers must call typed namespace methods (e.g. `session.client.mark.archive(...)`).
999
+ *
1000
+ * @returns disposer that unsubscribes the handler.
1001
+ */
1002
+ subscribe<K extends keyof EventMap>(channel: K, handler: (payload: EventMap[K]) => void): () => void;
1003
+ dispose(): Promise<void>;
1004
+ }
1005
+
1006
+ /**
1007
+ * OpenResource — a single entry in the open-resources list (tabs).
1008
+ *
1009
+ * The list itself lives on `SemiontBrowser.openResources$`. The CRUD
1010
+ * methods (`addOpenResource`, `removeOpenResource`, `updateOpenResourceName`,
1011
+ * `reorderOpenResources`) live on `SemiontBrowser` too.
1012
+ */
1013
+ interface OpenResource {
1014
+ /** Unique identifier for the resource */
1015
+ id: string;
1016
+ /** Display name of the resource */
1017
+ name: string;
1018
+ /** Timestamp when the resource was opened */
1019
+ openedAt: number;
1020
+ /** Order/position for manual sorting (optional for backward compatibility) */
1021
+ order?: number;
1022
+ /** Media type for icon display (e.g., 'application/pdf', 'text/plain') */
1023
+ mediaType?: string;
1024
+ /** Working-tree URI (e.g. "file://docs/overview.md") — used as tooltip in navigation */
1025
+ storageUri?: string;
1026
+ }
1027
+
1028
+ /**
1029
+ * FrontendSessionSignals — modal state that belongs to the UI, not
1030
+ * the session itself.
1031
+ *
1032
+ * `SemiontSession` is a headless per-backend client + token + user
1033
+ * holder. It can run in any process: browser, worker, CLI, test. But
1034
+ * the session-expired / permission-denied *modals* only make sense
1035
+ * in a UI context. Keeping those observables on `SemiontSession`
1036
+ * meant workers and CLIs carried four dead BehaviorSubjects that
1037
+ * nothing would ever fire.
1038
+ *
1039
+ * `FrontendSessionSignals` owns the modal state and has no hard
1040
+ * reference to a session. `SemiontBrowser` constructs one alongside
1041
+ * every frontend session and wires:
1042
+ *
1043
+ * - `session.onAuthFailed` → `signals.notifySessionExpired` so
1044
+ * proactive-refresh failures surface as modals
1045
+ * - `notify` module handlers → the active signals' methods so
1046
+ * external callers (e.g. React Query's QueryCache.onError) can
1047
+ * trigger the modals without touching the session
1048
+ *
1049
+ * React consumers that need modal state subscribe here; consumers
1050
+ * that need bus/HTTP access continue to subscribe to the session.
1051
+ *
1052
+ * Session auth-state cleanup (clearing token, clearing storage) is
1053
+ * the session's own responsibility inside `refresh()` — by the time
1054
+ * `notifySessionExpired` runs, the session has already torn down.
1055
+ * Signals only surfaces the modal.
1056
+ */
1057
+
1058
+ declare class FrontendSessionSignals {
1059
+ readonly sessionExpiredAt$: BehaviorSubject<number | null>;
1060
+ readonly sessionExpiredMessage$: BehaviorSubject<string | null>;
1061
+ readonly permissionDeniedAt$: BehaviorSubject<number | null>;
1062
+ readonly permissionDeniedMessage$: BehaviorSubject<string | null>;
1063
+ constructor();
1064
+ notifySessionExpired(message: string | null): void;
1065
+ notifyPermissionDenied(message: string | null): void;
1066
+ acknowledgeSessionExpired(): void;
1067
+ acknowledgePermissionDenied(): void;
1068
+ dispose(): void;
1069
+ }
1070
+
1071
+ /**
1072
+ * SemiontBrowser — top-level app-facing container for non-KB state.
1073
+ *
1074
+ * Holds the list of configured KBs, the active KB selection, the active
1075
+ * SemiontSession, the identity token, the open-resources list, and a
1076
+ * session-level error stream. Module-scoped singleton — survives every
1077
+ * React re-render, remount, and route change. `SemiontProvider` hands
1078
+ * the singleton to the React tree; `useSemiont()` returns it.
1079
+ *
1080
+ * Persistence goes through a `SessionStorage` adapter provided at
1081
+ * construction — the browser never touches `localStorage` or `window`
1082
+ * directly.
1083
+ */
1084
+
1085
+ interface SemiontBrowserConfig {
1086
+ /** Persistence adapter. The browser reads/writes all persisted state via this. */
1087
+ storage: SessionStorage;
1088
+ }
1089
+ declare class SemiontBrowser {
1090
+ readonly kbs$: BehaviorSubject<KnowledgeBase[]>;
1091
+ readonly activeKbId$: BehaviorSubject<string | null>;
1092
+ readonly activeSession$: BehaviorSubject<SemiontSession | null>;
1093
+ /**
1094
+ * Modal signals (session-expired / permission-denied) for the
1095
+ * currently-active session. Parallels `activeSession$` — always
1096
+ * non-null when `activeSession$` is non-null, always null when it
1097
+ * is. Extracted from the session itself so headless sessions
1098
+ * (workers, CLIs, tests) don't carry dead modal observables.
1099
+ * See [FrontendSessionSignals](./frontend-session-signals.ts).
1100
+ */
1101
+ readonly activeSignals$: BehaviorSubject<FrontendSessionSignals | null>;
1102
+ /**
1103
+ * True while a session is actively being constructed (setActiveKb /
1104
+ * signIn in flight, awaiting `session.ready`). Distinguishes the
1105
+ * "session about to arrive" intermediate state from "session
1106
+ * intentionally null" (after signOut, or when the active KB has no
1107
+ * stored credentials). UIs that want a loading spinner should gate
1108
+ * on this; otherwise they get stuck spinning after every signOut.
1109
+ */
1110
+ readonly sessionActivating$: BehaviorSubject<boolean>;
1111
+ readonly openResources$: BehaviorSubject<OpenResource[]>;
1112
+ readonly error$: Subject<SemiontError>;
1113
+ readonly identityToken$: BehaviorSubject<string | null>;
1114
+ private readonly storage;
1115
+ /**
1116
+ * App-scoped EventBus. Hosts UI-shell events that must work regardless
1117
+ * of whether a KB session is active: panel toggles, sidebar state,
1118
+ * tab reorders, routing, settings, etc. Disjoint from the per-session
1119
+ * bus inside `SemiontClient`, which carries KB-content events
1120
+ * (mark:*, beckon:*, gather:*, match:*, bind:*, yield:*, browse:click).
1121
+ */
1122
+ private readonly eventBus;
1123
+ private unregisterNotify;
1124
+ private unsubscribeStorage;
1125
+ private disposed;
1126
+ private activating;
1127
+ /**
1128
+ * Per-KB in-flight refresh dedup. Simultaneous 401s for the same
1129
+ * KB converge on a single `/api/tokens/refresh` network call.
1130
+ * Was previously module-scoped in `refresh.ts`; moved here when
1131
+ * that file was deleted — SemiontBrowser is a singleton so the
1132
+ * scoping is equivalent.
1133
+ */
1134
+ private readonly inFlightRefreshes;
1135
+ constructor(config: SemiontBrowserConfig);
1136
+ /** Emit an event on the browser's app-scoped bus. */
1137
+ emit<K extends keyof EventMap>(channel: K, payload: EventMap[K]): void;
1138
+ /** Subscribe to an event; returns unsubscribe. */
1139
+ on<K extends keyof EventMap>(channel: K, handler: (payload: EventMap[K]) => void): () => void;
1140
+ /** Read-only observable for an app-scoped channel. */
1141
+ stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]>;
1142
+ /**
1143
+ * Set the app-level identity token (from NextAuth's useSession).
1144
+ * Called at the root layout via a single `useEffect`. No other site
1145
+ * in the codebase should call this.
1146
+ */
1147
+ setIdentityToken(token: string | null): void;
1148
+ addKb(input: NewKnowledgeBase, access: string, refresh: string): KnowledgeBase;
1149
+ removeKb(id: string): void;
1150
+ updateKb(id: string, updates: Partial<KnowledgeBase>): void;
1151
+ /**
1152
+ * Read the locally-stored credential status for a KB. Pure / synchronous —
1153
+ * does not subscribe to context changes. Used by KB-list UI to color status
1154
+ * dots without requiring re-renders on every tick.
1155
+ */
1156
+ getKbSessionStatus(kbId: string): KbSessionStatus;
1157
+ /**
1158
+ * Switch the active KB. Follows the D2 disposal contract:
1159
+ * 1. Synchronously announce the new id on `activeKbId$` and null out
1160
+ * `activeSession$` so views see a safe empty state first.
1161
+ * 2. Serialize overlapping calls — if an activation is in flight, wait
1162
+ * for it before proceeding.
1163
+ * 3. Dispose whatever session is currently live.
1164
+ * 4. Construct the next session and await `session.ready`.
1165
+ * 5. Before emitting, re-check `activeKbId$` — if a newer call superseded
1166
+ * us while we waited, dispose our session and skip the emit.
1167
+ * 6. Emit the new session.
1168
+ */
1169
+ setActiveKb(id: string | null): Promise<void>;
1170
+ /**
1171
+ * Sign in to an existing KB: store the tokens and (re)activate the
1172
+ * session. If the KB is already active, the current session is disposed
1173
+ * and replaced so the new tokens take effect.
1174
+ */
1175
+ signIn(id: string, access: string, refresh: string): Promise<void>;
1176
+ /**
1177
+ * Sign out of a KB: clear stored tokens. If the KB is active, dispose
1178
+ * its session + signals and emit null for both.
1179
+ */
1180
+ signOut(id: string): Promise<void>;
1181
+ addOpenResource(id: string, name: string, mediaType?: string, storageUri?: string): void;
1182
+ removeOpenResource(id: string): void;
1183
+ updateOpenResourceName(id: string, name: string): void;
1184
+ reorderOpenResources(oldIndex: number, newIndex: number): void;
1185
+ /**
1186
+ * Refresh the active KB's access token. Returns the new token on
1187
+ * success, null on failure. Concurrent calls for the same KB
1188
+ * dedupe through `inFlightRefreshes`, so simultaneous 401s trigger
1189
+ * only one `/api/tokens/refresh` round trip.
1190
+ *
1191
+ * Uses a throwaway `SemiontClient` with no `tokenRefresher` —
1192
+ * a refresh call returning 401 would otherwise re-enter this
1193
+ * function infinitely.
1194
+ */
1195
+ private performRefresh;
1196
+ /**
1197
+ * Validate an access token by calling `auth.me` on a throwaway
1198
+ * client. The session uses this once at startup to populate
1199
+ * `user$`; 401 triggers a refresh-then-retry inside the session.
1200
+ *
1201
+ * The throwaway transport is seeded with the specific token to
1202
+ * validate so the request actually carries it (HttpTransport
1203
+ * sources `Authorization` from its `token$`).
1204
+ */
1205
+ private performValidate;
1206
+ dispose(): Promise<void>;
1207
+ }
1208
+
1209
+ /**
1210
+ * Module-scoped singleton for SemiontBrowser. Constructed lazily on first
1211
+ * `getBrowser()` call, survives every React re-render, remount, and route
1212
+ * change.
1213
+ *
1214
+ * The caller provides a `SessionStorage` implementation — there is no
1215
+ * default here because storage-backend selection is environment-specific
1216
+ * (browsers use `WebBrowserStorage`, CLI uses a filesystem adapter,
1217
+ * tests use `InMemorySessionStorage`). The first call to `getBrowser`
1218
+ * wins; subsequent calls return the cached instance regardless of the
1219
+ * storage passed.
1220
+ */
1221
+
1222
+ interface GetBrowserOptions {
1223
+ /** Persistence adapter used to construct the singleton on first call. */
1224
+ storage: SessionStorage;
1225
+ }
1226
+ declare function getBrowser(options: GetBrowserOptions): SemiontBrowser;
1227
+
1228
+ /**
1229
+ * Pure helpers and storage-adapter-driven loaders for the Semiont
1230
+ * session layer.
1231
+ *
1232
+ * Contains:
1233
+ * - Storage key shape (constants, `sessionKey(kbId)`)
1234
+ * - JWT expiry parsing and "is expired" check
1235
+ * - URL/protocol helpers for KB instances
1236
+ * - Loaders/savers that take a `SessionStorage` and operate over it
1237
+ * (no direct `localStorage` access)
1238
+ *
1239
+ * No React imports, no module-scoped state, no side effects beyond
1240
+ * whatever the passed-in `SessionStorage` does.
1241
+ */
1242
+
1243
+ /** The shape persisted per KB. */
1244
+ interface StoredSession {
1245
+ access: string;
1246
+ refresh: string;
1247
+ }
1248
+ declare function setStoredSession(storage: SessionStorage, kbId: string, session: StoredSession): void;
1249
+ declare function defaultProtocol(host: string): 'http' | 'https';
1250
+ declare function isValidHostname(host: string): boolean;
1251
+ declare function kbBackendUrl(kb: KnowledgeBase): string;
1252
+
1253
+ declare function notifySessionExpired(message?: string): void;
1254
+ declare function notifyPermissionDenied(message?: string): void;
1255
+
1256
+ interface ViewModel {
1257
+ dispose(): void;
1258
+ }
1259
+ declare function createDisposer(): {
1260
+ add(vm: ViewModel | (() => void)): void;
1261
+ dispose(): void;
1262
+ };
1263
+
1264
+ /**
1265
+ * createSearchPipeline
1266
+ *
1267
+ * A debounced-search RxJS pipeline factory. Combines an input Subject with a
1268
+ * downstream fetch function and emits typed `{ results, isSearching }` state.
1269
+ *
1270
+ * Designed to be created once per component instance via React's
1271
+ * `useState(() => createSearchPipeline(...))` lazy initializer, then consumed
1272
+ * via `useObservable(pipeline.state$)`. The pipeline holds no React state and
1273
+ * has no React imports — it's pure RxJS, unit-testable without a renderer.
1274
+ *
1275
+ * The fetch function is expected to return `Observable<T[] | undefined>`,
1276
+ * matching the cache-miss-then-data shape of `BrowseNamespace` Observables:
1277
+ * `undefined` means "fetch in flight, no value yet"; an array means "data
1278
+ * available (possibly empty)".
1279
+ */
1280
+
1281
+ interface SearchState<T> {
1282
+ results: T[];
1283
+ isSearching: boolean;
1284
+ }
1285
+ interface SearchPipeline<T> {
1286
+ /** Latest query string. Bind to a controlled input via `useObservable`. */
1287
+ query$: Observable<string>;
1288
+ /** Latest search state — results plus a loading flag. */
1289
+ state$: Observable<SearchState<T>>;
1290
+ /** Push a new query value. Triggers the debounced fetch. */
1291
+ setQuery(value: string): void;
1292
+ /** Tear down the input Subject. Call from `useEffect` cleanup. */
1293
+ dispose(): void;
1294
+ }
1295
+ interface SearchPipelineOptions {
1296
+ /** Milliseconds to wait after the last keystroke before fetching. Default 250. */
1297
+ debounceMs?: number;
1298
+ /** Initial query value. Useful for modals that open with a pre-filled term. */
1299
+ initialQuery?: string;
1300
+ }
1301
+ declare function createSearchPipeline<T>(fetch: (query: string) => Observable<T[] | undefined>, options?: SearchPipelineOptions): SearchPipeline<T>;
1302
+
1303
+ interface BeckonVM extends ViewModel {
1304
+ hoveredAnnotationId$: Observable<AnnotationId | null>;
1305
+ hover(annotationId: AnnotationId | null): void;
1306
+ focus(annotationId: AnnotationId): void;
1307
+ sparkle(annotationId: AnnotationId): void;
1308
+ }
1309
+ declare function createBeckonVM(client: SemiontClient): BeckonVM;
1310
+ /** Default milliseconds the mouse must dwell before beckon:hover is emitted. */
1311
+ declare const HOVER_DELAY_MS = 150;
1312
+ type EmitHover = (annotationId: AnnotationId | null) => void;
1313
+ interface HoverHandlers {
1314
+ handleMouseEnter: (annotationId: AnnotationId) => void;
1315
+ handleMouseLeave: () => void;
1316
+ cleanup: () => void;
1317
+ }
1318
+ declare function createHoverHandlers(emit: EmitHover, delayMs: number): HoverHandlers;
1319
+
1320
+ /**
1321
+ * ShellVM — app-shell state: which toolbar panel is open, tab-bar
1322
+ * coordination helpers, scroll-to-annotation signals. Lives on
1323
+ * `SemiontBrowser`'s app-scoped bus (not the per-session client bus)
1324
+ * because panel toggles and shell chrome must work regardless of
1325
+ * whether a KB session is active.
1326
+ *
1327
+ * Channels: `panel:toggle`, `panel:open`, `panel:close`.
1328
+ */
1329
+
1330
+ type ToolbarPanelType = 'history' | 'info' | 'annotations' | 'settings' | 'collaboration' | 'user' | 'jsonld' | 'knowledge-base';
1331
+ declare const COMMON_PANELS: readonly ToolbarPanelType[];
1332
+ declare const RESOURCE_PANELS: readonly ToolbarPanelType[];
1333
+ interface ShellVM extends ViewModel {
1334
+ activePanel$: Observable<ToolbarPanelType | null>;
1335
+ scrollToAnnotationId$: Observable<string | null>;
1336
+ panelInitialTab$: Observable<{
1337
+ tab: string;
1338
+ generation: number;
1339
+ } | null>;
1340
+ openPanel(panel: string): void;
1341
+ closePanel(): void;
1342
+ togglePanel(panel: string): void;
1343
+ onScrollCompleted(): void;
1344
+ }
1345
+ interface ShellVMOptions {
1346
+ initialPanel?: ToolbarPanelType | null;
1347
+ onPanelChange?: (panel: ToolbarPanelType | null) => void;
1348
+ }
1349
+ declare function createShellVM(browser: SemiontBrowser, options?: ShellVMOptions): ShellVM;
1350
+
1351
+ interface GatherVM extends ViewModel {
1352
+ context$: Observable<GatheredContext | null>;
1353
+ loading$: Observable<boolean>;
1354
+ error$: Observable<Error | null>;
1355
+ annotationId$: Observable<AnnotationId | null>;
1356
+ }
1357
+ declare function createGatherVM(client: SemiontClient, resourceId: ResourceId): GatherVM;
1358
+
1359
+ interface MatchVM extends ViewModel {
1360
+ }
1361
+ declare function createMatchVM(client: SemiontClient, _resourceId: ResourceId): MatchVM;
1362
+
1363
+ type JobProgress$1 = components['schemas']['JobProgress'];
1364
+ interface GenerateDocumentOptions {
1365
+ title: string;
1366
+ storageUri: string;
1367
+ prompt?: string;
1368
+ language?: string;
1369
+ temperature?: number;
1370
+ maxTokens?: number;
1371
+ context: GatheredContext;
1372
+ }
1373
+ interface YieldVM extends ViewModel {
1374
+ isGenerating$: Observable<boolean>;
1375
+ progress$: Observable<JobProgress$1 | null>;
1376
+ generate(referenceId: string, options: GenerateDocumentOptions): void;
1377
+ }
1378
+ declare function createYieldVM(client: SemiontClient, resourceId: ResourceId, locale: string): YieldVM;
1379
+
1380
+ type JobProgress = components['schemas']['JobProgress'];
1381
+ interface PendingAnnotation {
1382
+ selector: Selector | Selector[];
1383
+ motivation: Motivation;
1384
+ }
1385
+ interface MarkVM extends ViewModel {
1386
+ pendingAnnotation$: Observable<PendingAnnotation | null>;
1387
+ assistingMotivation$: Observable<Motivation | null>;
1388
+ progress$: Observable<JobProgress | null>;
1389
+ }
1390
+ declare function createMarkVM(client: SemiontClient, resourceId: ResourceId): MarkVM;
1391
+
1392
+ interface DiscoverVM extends ViewModel {
1393
+ browse: ShellVM;
1394
+ search: SearchPipeline<ResourceDescriptor>;
1395
+ recentResources$: Observable<ResourceDescriptor[]>;
1396
+ entityTypes$: Observable<string[]>;
1397
+ isLoadingRecent$: Observable<boolean>;
1398
+ }
1399
+ declare function createDiscoverVM(client: SemiontClient, browse: ShellVM): DiscoverVM;
1400
+
1401
+ interface EntityTagsVM extends ViewModel {
1402
+ browse: ShellVM;
1403
+ entityTypes$: Observable<string[]>;
1404
+ isLoading$: Observable<boolean>;
1405
+ newTag$: Observable<string>;
1406
+ error$: Observable<string>;
1407
+ isAdding$: Observable<boolean>;
1408
+ setNewTag(value: string): void;
1409
+ addTag(): Promise<void>;
1410
+ }
1411
+ declare function createEntityTagsVM(client: SemiontClient, browse: ShellVM): EntityTagsVM;
1412
+
1413
+ interface ImportPreview {
1414
+ format: string;
1415
+ version: number;
1416
+ sourceUrl: string;
1417
+ stats: Record<string, number>;
1418
+ }
1419
+ interface ExchangeVM extends ViewModel {
1420
+ browse: ShellVM;
1421
+ selectedFile$: Observable<File | null>;
1422
+ preview$: Observable<ImportPreview | null>;
1423
+ importPhase$: Observable<string | null>;
1424
+ importMessage$: Observable<string | undefined>;
1425
+ importResult$: Observable<Record<string, unknown> | undefined>;
1426
+ isExporting$: Observable<boolean>;
1427
+ isImporting$: Observable<boolean>;
1428
+ selectFile(file: File): void;
1429
+ cancelImport(): void;
1430
+ doExport(): Promise<{
1431
+ blob: Blob;
1432
+ filename: string;
1433
+ }>;
1434
+ doImport(): Promise<void>;
1435
+ }
1436
+ declare function createExchangeVM(browse: ShellVM, exportFn: (params?: {
1437
+ includeArchived?: boolean;
1438
+ }) => Promise<Response>, importFn: (file: File, options?: {
1439
+ onProgress?: (event: {
1440
+ phase: string;
1441
+ message?: string;
1442
+ result?: Record<string, unknown>;
1443
+ }) => void;
1444
+ }) => Promise<{
1445
+ phase: string;
1446
+ message?: string;
1447
+ result?: Record<string, unknown>;
1448
+ }>): ExchangeVM;
1449
+
1450
+ interface AdminUsersVM extends ViewModel {
1451
+ browse: ShellVM;
1452
+ users$: Observable<unknown[]>;
1453
+ stats$: Observable<unknown | null>;
1454
+ usersLoading$: Observable<boolean>;
1455
+ statsLoading$: Observable<boolean>;
1456
+ updateUser(id: string, data: {
1457
+ isAdmin?: boolean;
1458
+ isActive?: boolean;
1459
+ }): Promise<void>;
1460
+ }
1461
+ declare function createAdminUsersVM(client: SemiontClient, browse: ShellVM): AdminUsersVM;
1462
+
1463
+ interface AdminSecurityVM extends ViewModel {
1464
+ browse: ShellVM;
1465
+ providers$: Observable<unknown[]>;
1466
+ allowedDomains$: Observable<string[]>;
1467
+ isLoading$: Observable<boolean>;
1468
+ }
1469
+ declare function createAdminSecurityVM(client: SemiontClient, browse: ShellVM): AdminSecurityVM;
1470
+
1471
+ interface WelcomeVM extends ViewModel {
1472
+ userData$: Observable<{
1473
+ termsAcceptedAt?: string;
1474
+ } | null>;
1475
+ isProcessing$: Observable<boolean>;
1476
+ acceptTerms(): Promise<void>;
1477
+ }
1478
+ declare function createWelcomeVM(client: SemiontClient): WelcomeVM;
1479
+
1480
+ interface ResourceLoaderVM extends ViewModel {
1481
+ resource$: Observable<ResourceDescriptor | undefined>;
1482
+ isLoading$: Observable<boolean>;
1483
+ invalidate(): void;
1484
+ }
1485
+ declare function createResourceLoaderVM(client: SemiontClient, resourceId: ResourceId): ResourceLoaderVM;
1486
+
1487
+ interface SessionVM extends ViewModel {
1488
+ isLoggingOut$: Observable<boolean>;
1489
+ logout(): Promise<void>;
1490
+ }
1491
+ declare function createSessionVM(client: SemiontClient): SessionVM;
1492
+
1493
+ interface SmelterEvent {
1494
+ type: string;
1495
+ resourceId?: string;
1496
+ payload: Record<string, unknown>;
1497
+ }
1498
+ interface SmelterActorVMOptions {
1499
+ baseUrl: string;
1500
+ token: string;
1501
+ reconnectMs?: number;
1502
+ }
1503
+ interface SmelterActorVM extends ViewModel {
1504
+ events$: Observable<SmelterEvent>;
1505
+ state$: Observable<ConnectionState>;
1506
+ emit(channel: string, payload: Record<string, unknown>): Promise<void>;
1507
+ start(): void;
1508
+ stop(): void;
1509
+ }
1510
+ declare function createSmelterActorVM(options: SmelterActorVMOptions): SmelterActorVM;
1511
+
1512
+ /**
1513
+ * Job Claim Adapter — worker-side job lifecycle glue on top of a
1514
+ * shared `ActorVM`.
1515
+ *
1516
+ * Replaces the old `WorkerVM`, which owned its own actor and
1517
+ * duplicated the SSE connection that `SemiontClient` already held.
1518
+ * Workers now construct a `SemiontSession` normally (one actor, one
1519
+ * SSE connection) and use this adapter to attach job-claim behaviour
1520
+ * on top of `session.client.actor`.
1521
+ *
1522
+ * The adapter is intentionally thin: it subscribes to `job:queued`
1523
+ * on the actor, claims jobs via the existing request-response
1524
+ * protocol (`job:claim` → `job:claimed` / `job:claim-failed`), and
1525
+ * exposes observables for job orchestration. It does **not** own
1526
+ * the actor, has no HTTP concerns, and has no modal state.
1527
+ */
1528
+
1529
+ interface JobAssignment {
1530
+ jobId: string;
1531
+ type: string;
1532
+ resourceId: string;
1533
+ }
1534
+ interface ActiveJob {
1535
+ jobId: string;
1536
+ type: string;
1537
+ resourceId: string;
1538
+ userId: string;
1539
+ params: Record<string, unknown>;
1540
+ }
1541
+ interface JobClaimAdapterOptions {
1542
+ /** Shared actor (typically `session.client.actor`). */
1543
+ actor: ActorVM;
1544
+ /**
1545
+ * Job types this worker can process. Jobs of other types that
1546
+ * arrive on `job:queued` are ignored. Empty array = accept any.
1547
+ */
1548
+ jobTypes: string[];
1549
+ }
1550
+ interface JobClaimAdapter {
1551
+ /** Currently-claimed job, or null when idle. */
1552
+ readonly activeJob$: Observable<ActiveJob | null>;
1553
+ /** True while a claim is in flight or a job is being processed. */
1554
+ readonly isProcessing$: Observable<boolean>;
1555
+ /** Monotonically-incrementing count of successfully-completed jobs. */
1556
+ readonly jobsCompleted$: Observable<number>;
1557
+ /** Stream of job failures (including claim-failed and processing errors). */
1558
+ readonly errors$: Observable<{
1559
+ jobId: string;
1560
+ error: string;
1561
+ }>;
1562
+ /**
1563
+ * Subscribe to `job:queued` events (adding the channel to the actor
1564
+ * if not already subscribed) and begin claiming matching jobs.
1565
+ * Idempotent — calling `start()` twice is a no-op.
1566
+ */
1567
+ start(): void;
1568
+ /** Stop claiming new jobs. Does not cancel an in-flight job. */
1569
+ stop(): void;
1570
+ /** Signal successful completion of `activeJob$`. */
1571
+ completeJob(): void;
1572
+ /** Signal failure of `activeJob$`. Emits on `errors$`. */
1573
+ failJob(jobId: string, error: string): void;
1574
+ /** Release observables. Does not dispose the shared actor. */
1575
+ dispose(): void;
1576
+ }
1577
+ /**
1578
+ * Attach job-claim behaviour to a shared `ActorVM`.
1579
+ */
1580
+ declare function createJobClaimAdapter(options: JobClaimAdapterOptions): JobClaimAdapter;
1581
+
1582
+ interface Job {
1583
+ jobId: string;
1584
+ type: string;
1585
+ status: string;
1586
+ resourceId: string;
1587
+ /** DID of the user who initiated the job (audit). */
1588
+ userId: string;
1589
+ created: string;
1590
+ startedAt?: string;
1591
+ completedAt?: string;
1592
+ error?: string;
1593
+ progress?: Record<string, unknown>;
1594
+ result?: Record<string, unknown>;
1595
+ }
1596
+ interface JobQueueVM extends ViewModel {
1597
+ jobs$: Observable<Job[]>;
1598
+ pendingByType$: Observable<Map<string, number>>;
1599
+ runningJobs$: Observable<Job[]>;
1600
+ jobCreated$: Observable<Job>;
1601
+ jobCompleted$: Observable<Job>;
1602
+ jobFailed$: Observable<Job>;
1603
+ }
1604
+ declare function createJobQueueVM(client: SemiontClient): JobQueueVM;
1605
+
1606
+ interface AnnotationGroups {
1607
+ highlights: Annotation[];
1608
+ comments: Annotation[];
1609
+ assessments: Annotation[];
1610
+ references: Annotation[];
1611
+ tags: Annotation[];
1612
+ }
1613
+ type StoredEventResponse = components['schemas']['StoredEventResponse'];
1614
+ interface WizardState {
1615
+ open: boolean;
1616
+ annotationId: string | null;
1617
+ resourceId: string | null;
1618
+ defaultTitle: string;
1619
+ entityTypes: string[];
1620
+ }
1621
+ interface ResourceViewerPageVM extends ViewModel {
1622
+ beckon: BeckonVM;
1623
+ browse: ShellVM;
1624
+ mark: MarkVM;
1625
+ gather: GatherVM;
1626
+ yield: YieldVM;
1627
+ annotations$: Observable<Annotation[]>;
1628
+ annotationGroups$: Observable<AnnotationGroups>;
1629
+ entityTypes$: Observable<string[]>;
1630
+ events$: Observable<StoredEventResponse[]>;
1631
+ referencedBy$: Observable<ReferencedByEntry[]>;
1632
+ content$: Observable<string>;
1633
+ contentLoading$: Observable<boolean>;
1634
+ mediaToken$: Observable<string | null>;
1635
+ wizard$: Observable<WizardState>;
1636
+ closeWizard(): void;
1637
+ }
1638
+ declare function createResourceViewerPageVM(client: SemiontClient, resourceId: ResourceId, locale: string, browse: ShellVM, options?: {
1639
+ mediaType?: string;
1640
+ }): ResourceViewerPageVM;
1641
+
1642
+ type ComposeMode = 'new' | 'clone' | 'reference';
1643
+ interface ComposeParams {
1644
+ mode?: string | undefined;
1645
+ token?: string | undefined;
1646
+ annotationUri?: string | undefined;
1647
+ sourceDocumentId?: string | undefined;
1648
+ name?: string | undefined;
1649
+ entityTypes?: string | undefined;
1650
+ storedContext?: string | undefined;
1651
+ }
1652
+ interface CloneData {
1653
+ sourceResource: ResourceDescriptor;
1654
+ sourceContent: string;
1655
+ }
1656
+ interface ReferenceData {
1657
+ annotationUri: string;
1658
+ sourceDocumentId: string;
1659
+ name: string;
1660
+ entityTypes: string[];
1661
+ }
1662
+ interface SaveResourceParams {
1663
+ mode: ComposeMode;
1664
+ name: string;
1665
+ storageUri: string;
1666
+ content?: string;
1667
+ file?: File;
1668
+ format?: string;
1669
+ charset?: string;
1670
+ entityTypes?: string[];
1671
+ language: string;
1672
+ archiveOriginal?: boolean;
1673
+ annotationUri?: string;
1674
+ sourceDocumentId?: string;
1675
+ }
1676
+ interface ComposePageVM extends ViewModel {
1677
+ browse: ShellVM;
1678
+ mode$: Observable<ComposeMode>;
1679
+ loading$: Observable<boolean>;
1680
+ cloneData$: Observable<CloneData | null>;
1681
+ referenceData$: Observable<ReferenceData | null>;
1682
+ gatheredContext$: Observable<GatheredContext | null>;
1683
+ entityTypes$: Observable<string[]>;
1684
+ save(params: SaveResourceParams): Promise<string>;
1685
+ }
1686
+ declare function createComposePageVM(client: SemiontClient, browse: ShellVM, params: ComposeParams, auth?: AccessToken): ComposePageVM;
1687
+
1688
+ export { type ActiveJob, AdminNamespace, type AdminSecurityVM, type AdminUsersVM, type AnnotationGroups, type AnnotationHistoryResponse, AuthNamespace, BeckonNamespace, type BeckonVM, BindNamespace, BrowseNamespace, BusRequestError, type BusRequestPrimitive, COMMON_PANELS, type Cache, type CloneData, type ComposeMode, type ComposePageVM, type ComposeParams, type CreateAnnotationInput, type CreateFromTokenOptions, type CreateResourceInput, 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 MarkAssistEvent, type MarkAssistOptions, type MarkAssistProgress, MarkNamespace, type MarkVM, MatchNamespace, type MatchSearchProgress, type MatchVM, type NewKnowledgeBase, type OpenResource, type PendingAnnotation, RESOURCE_PANELS, type ReferenceData, type ReferencedByEntry, type RequestContent, type ResourceLoaderVM, type ResourceViewerPageVM, type ResponseContent, type SaveResourceParams, type SearchPipeline, type SearchPipelineOptions, type SearchState, SemiontBrowser, type SemiontBrowserConfig, SemiontClient, SemiontError, type SemiontErrorCode, SemiontSession, type SemiontSessionConfig, type SessionStorage, type SessionVM, type ShellVM, type ShellVMOptions, type SmelterActorVM, type SmelterActorVMOptions, type SmelterEvent, type StoredSession, type ToolbarPanelType, type User, type UserInfo, type ViewModel, type WelcomeVM, type WizardState, type YieldGenerationEvent, YieldNamespace, type YieldVM, busRequest, 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, isValidHostname, kbBackendUrl, notifyPermissionDenied, notifySessionExpired, setStoredSession };