@semiont/sdk 0.5.1 → 0.5.2

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
@@ -2,10 +2,9 @@ import * as rxjs from 'rxjs';
2
2
  import { Observable, BehaviorSubject, Subject } from 'rxjs';
3
3
  export { firstValueFrom, lastValueFrom } from 'rxjs';
4
4
  import * as _semiont_core from '@semiont/core';
5
- import { components, UserDID, paths, ResourceId, AnnotationId, BodyOperation, EventMap, ResourceDescriptor, Annotation, GraphConnection, Motivation, GatheredContext, JobId, ITransport, EventBus, IContentTransport, BaseUrl, AccessToken, SemiontError, ConnectionState, Selector } from '@semiont/core';
5
+ import { ResourceId, components, UserDID, paths, BackendDownload, ProgressEvent, AnnotationId, BodyOperation, EventMap, ResourceDescriptor, Annotation, GraphConnection, Motivation, GatheredContext, JobId, ITransport, EventBus, IContentTransport, IBackendOperations, BaseUrl, AccessToken, SemiontError, ConnectionState, Selector } from '@semiont/core';
6
6
  export { AccessToken, Annotation, AnnotationId, BaseUrl, BodyItem, BodyOperation, ConnectionState, EntityType, EventMap, GatheredContext, IContentTransport, ITransport, Logger, Motivation, RefreshToken, ResourceDescriptor, ResourceId, SemiontError, UserId, accessToken, annotationId, baseUrl, entityType, refreshToken, resourceId, userId } from '@semiont/core';
7
- import { ActorVM } from '@semiont/api-client';
8
- export { APIError, APIErrorCode, ActorVM, ActorVMOptions, BusEvent, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, HttpTransportConfig, TokenRefresher, createActorVM } from '@semiont/api-client';
7
+ export { APIError, HttpContentTransport, HttpTransport, HttpTransportConfig, TokenRefresher } from '@semiont/api-client';
9
8
 
10
9
  /**
11
10
  * Thenable Observable subclasses.
@@ -15,8 +14,8 @@ export { APIError, APIErrorCode, ActorVM, ActorVMOptions, BusEvent, DEGRADED_THR
15
14
  * lifecycle, generation progress) and cache reads (Browse live queries).
16
15
  *
17
16
  * The point: scripts can `await` the call directly without `lastValueFrom` /
18
- * `firstValueFrom` wrappers; reactive consumers (frontend view-models) keep
19
- * using `.subscribe(...)` and `.pipe(...)` exactly as before.
17
+ * `firstValueFrom` wrappers; reactive consumers keep using `.subscribe(...)`
18
+ * and `.pipe(...)` exactly as before.
20
19
  *
21
20
  * The asymmetric `.then()` semantics — last-value-on-completion for streams,
22
21
  * first-non-undefined-value for caches — is encoded by the subclass name. The
@@ -66,12 +65,48 @@ declare class CacheObservable<T> extends Observable<T | undefined> implements Pr
66
65
  * Observable per key (its B4 contract), so this preserves that contract
67
66
  * through the awaitable wrapping. Without the memo, every public-method
68
67
  * call would produce a fresh wrapper and break referential-equality
69
- * guarantees that React-side consumers depend on.
68
+ * guarantees that hook-style reactive consumers depend on.
70
69
  *
71
70
  * Backed by a `WeakMap`, so wrappers are GC'd when their source is.
72
71
  */
73
72
  static from<T>(source: Observable<T | undefined>): CacheObservable<T>;
74
73
  }
74
+ /**
75
+ * Discriminated phases of an upload's lifecycle.
76
+ *
77
+ * - `started` — emitted immediately on `yield.resource(...)` invocation, before any bytes flow.
78
+ * - `progress` — emitted as bytes flow over the wire. Wired by `HttpContentTransport`'s XHR path when a caller passes `onProgress` (or, transitively, when `yield.resource` is the caller — it always wires the hook so subscribers see byte counts). `bytesUploaded` and `totalBytes` carry the running counts; `totalBytes` may be 0 when the transport can't determine the total (rare, e.g. chunked encoding) — UI consumers should render an indeterminate state in that case.
79
+ * - `finished` — emitted on backend acknowledgement, carries the assigned `resourceId`.
80
+ *
81
+ * Failures surface as `Observable.error(...)` (typically an `APIError` from the transport's `errors$` Subject), not as a `phase: 'failed'` event — `subscribe`'s error callback handles them. Cancellation is honored: unsubscribing before `finished` aborts the in-flight HTTP request on the XHR path.
82
+ */
83
+ type UploadProgress = {
84
+ phase: 'started';
85
+ totalBytes: number;
86
+ } | {
87
+ phase: 'progress';
88
+ bytesUploaded: number;
89
+ totalBytes: number;
90
+ } | {
91
+ phase: 'finished';
92
+ resourceId: ResourceId;
93
+ };
94
+ /**
95
+ * Specialized `StreamObservable` for `yield.resource`. Subscribers see the
96
+ * full `UploadProgress` event sequence (started → optional progress → finished).
97
+ * Awaiting resolves specifically to `{ resourceId }` extracted from the
98
+ * `'finished'` event — preserving the pre-Phase-18 awaited shape so existing
99
+ * `await client.yield.resource(...)` callers don't need to narrow the union.
100
+ */
101
+ declare class UploadObservable extends Observable<UploadProgress> implements PromiseLike<{
102
+ resourceId: ResourceId;
103
+ }> {
104
+ then<R1 = {
105
+ resourceId: ResourceId;
106
+ }, R2 = never>(onfulfilled?: ((v: {
107
+ resourceId: ResourceId;
108
+ }) => R1 | PromiseLike<R1>) | null, onrejected?: ((e: unknown) => R2 | PromiseLike<R2>) | null): PromiseLike<R1 | R2>;
109
+ }
75
110
 
76
111
  /**
77
112
  * Verb Namespace Interfaces
@@ -101,7 +136,7 @@ declare class CacheObservable<T> extends Observable<T | undefined> implements Pr
101
136
  * does not propagate through pipe — by design).
102
137
  */
103
138
 
104
- type StoredEventResponse$2 = components['schemas']['StoredEventResponse'];
139
+ type StoredEventResponse$1 = components['schemas']['StoredEventResponse'];
105
140
  type GatherProgress = components['schemas']['GatherProgress'];
106
141
  type MatchSearchResult = components['schemas']['MatchSearchResult'];
107
142
  type JobProgress$2 = components['schemas']['JobProgress'];
@@ -168,7 +203,10 @@ interface GenerationOptions {
168
203
  storageUri: string;
169
204
  context: GatheredContext;
170
205
  prompt?: string;
206
+ /** Annotation/resource body locale — language the generated resource is written in (typically the user's UI locale). */
171
207
  language?: string;
208
+ /** Source-resource locale — language of the resource the annotation lives on, used in the prompt so the LLM understands embedded source-context snippets. BCP-47. */
209
+ sourceLanguage?: string;
172
210
  temperature?: number;
173
211
  maxTokens?: number;
174
212
  }
@@ -179,7 +217,10 @@ interface MarkAssistOptions {
179
217
  instructions?: string;
180
218
  density?: number;
181
219
  tone?: string;
220
+ /** Annotation body locale — language the LLM should write generated body text in (comment text, assessment text, tag/reference body language stamp). BCP-47. */
182
221
  language?: string;
222
+ /** Source-resource locale — language of the content being analyzed, used in the prompt so the LLM analyzes non-English source correctly. BCP-47. */
223
+ sourceLanguage?: string;
183
224
  schemaId?: string;
184
225
  categories?: string[];
185
226
  }
@@ -257,7 +298,7 @@ interface BrowseNamespace$1 {
257
298
  annotation(resourceId: ResourceId, annotationId: AnnotationId): CacheObservable<Annotation>;
258
299
  entityTypes(): CacheObservable<string[]>;
259
300
  referencedBy(resourceId: ResourceId): CacheObservable<ReferencedByEntry[]>;
260
- events(resourceId: ResourceId): CacheObservable<StoredEventResponse$2[]>;
301
+ events(resourceId: ResourceId): CacheObservable<StoredEventResponse$1[]>;
261
302
  resourceContent(resourceId: ResourceId): Promise<string>;
262
303
  resourceRepresentation(resourceId: ResourceId, options?: {
263
304
  accept?: string;
@@ -271,7 +312,7 @@ interface BrowseNamespace$1 {
271
312
  stream: ReadableStream<Uint8Array>;
272
313
  contentType: string;
273
314
  }>;
274
- resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse$2[]>;
315
+ resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse$1[]>;
275
316
  annotationHistory(resourceId: ResourceId, annotationId: AnnotationId): Promise<AnnotationHistoryResponse>;
276
317
  connections(resourceId: ResourceId): Promise<GraphConnection[]>;
277
318
  backlinks(resourceId: ResourceId): Promise<Annotation[]>;
@@ -281,7 +322,33 @@ interface BrowseNamespace$1 {
281
322
  navigateReference(resourceId: ResourceId): void;
282
323
  }
283
324
  /**
284
- * Markannotation CRUD, entity types, AI assist
325
+ * Frameschema-layer flow (the eighth flow).
326
+ *
327
+ * Frame operates on the KB's conceptual vocabulary — what *kinds* of
328
+ * things exist (entity types) and, in the future, what taxonomies are
329
+ * recognized (tag schemas), what relations are typed (predicate types),
330
+ * and how schemas are imported (ontology I/O). The other seven flows
331
+ * (yield, mark, match, bind, gather, browse, beckon) operate on
332
+ * content; Frame operates on the schema layer that content is expressed
333
+ * in.
334
+ *
335
+ * MVP scope is small: entity-type vocabulary writes only. Live reads of
336
+ * the entity-type vocabulary stay on Browse (`browse.entityTypes()` is
337
+ * a `CacheObservable<string[]>` consumed by 8+ call sites). Frame owns
338
+ * writes; Browse owns reads — the same asymmetry that already holds for
339
+ * resources and annotations.
340
+ *
341
+ * Backend actor: Stower
342
+ * Event prefix: frame:*
343
+ */
344
+ interface FrameNamespace$1 {
345
+ /** Add a single entity type to the KB's vocabulary. Idempotent — adding an existing type is a no-op. */
346
+ addEntityType(type: string): Promise<void>;
347
+ /** Add multiple entity types in one call. Convenience over a loop of `addEntityType`. */
348
+ addEntityTypes(types: string[]): Promise<void>;
349
+ }
350
+ /**
351
+ * Mark — annotation CRUD, AI assist, resource lifecycle
285
352
  *
286
353
  * Commands return Promises that resolve on HTTP acceptance (202).
287
354
  * Results appear on browse Observables via bus gateway.
@@ -291,17 +358,15 @@ interface BrowseNamespace$1 {
291
358
  * Event prefix: mark:*
292
359
  */
293
360
  interface MarkNamespace$1 {
294
- annotation(resourceId: ResourceId, input: CreateAnnotationInput): Promise<{
361
+ annotation(input: CreateAnnotationInput): Promise<{
295
362
  annotationId: AnnotationId;
296
363
  }>;
297
364
  delete(resourceId: ResourceId, annotationId: AnnotationId): Promise<void>;
298
- entityType(type: string): Promise<void>;
299
- entityTypes(types: string[]): Promise<void>;
300
365
  archive(resourceId: ResourceId): Promise<void>;
301
366
  unarchive(resourceId: ResourceId): Promise<void>;
302
367
  assist(resourceId: ResourceId, motivation: Motivation, options: MarkAssistOptions): StreamObservable<MarkAssistEvent>;
303
368
  request(selector: components['schemas']['MarkRequestedEvent']['selector'], motivation: Motivation): void;
304
- /** Fire-and-forget variant of `assist` — mark-vm orchestrates the call and its progress Observable. */
369
+ /** Fire-and-forget variant of `assist` — mark-state-unit orchestrates the call and its progress Observable. */
305
370
  requestAssist(motivation: Motivation, options: MarkAssistOptions, correlationId?: string): void;
306
371
  /** Submit the currently pending annotation with its selector and optional body. */
307
372
  submit(input: components['schemas']['MarkSubmitEvent']): void;
@@ -339,7 +404,7 @@ interface BindNamespace$1 {
339
404
  * Event prefix: gather:*
340
405
  */
341
406
  interface GatherNamespace$1 {
342
- annotation(annotationId: AnnotationId, resourceId: ResourceId, options?: {
407
+ annotation(resourceId: ResourceId, annotationId: AnnotationId, options?: {
343
408
  contextWindow?: number;
344
409
  }): StreamObservable<GatherAnnotationProgress>;
345
410
  resource(resourceId: ResourceId, options?: {
@@ -360,7 +425,7 @@ interface MatchNamespace$1 {
360
425
  limit?: number;
361
426
  useSemanticScoring?: boolean;
362
427
  }): StreamObservable<MatchSearchProgress>;
363
- /** Fire-and-forget variant: match-vm orchestrates the call and its result Observable. */
428
+ /** Fire-and-forget variant: match-state-unit orchestrates the call and its result Observable. */
364
429
  requestSearch(input: components['schemas']['MatchSearchRequest']): void;
365
430
  }
366
431
  /**
@@ -373,9 +438,7 @@ interface MatchNamespace$1 {
373
438
  * Event prefix: yield:*
374
439
  */
375
440
  interface YieldNamespace$1 {
376
- resource(data: CreateResourceInput): Promise<{
377
- resourceId: string;
378
- }>;
441
+ resource(data: CreateResourceInput): UploadObservable;
379
442
  fromAnnotation(resourceId: ResourceId, annotationId: AnnotationId, options: GenerationOptions): StreamObservable<YieldGenerationEvent>;
380
443
  cloneToken(resourceId: ResourceId): Promise<{
381
444
  token: string;
@@ -383,7 +446,7 @@ interface YieldNamespace$1 {
383
446
  }>;
384
447
  fromToken(token: string): Promise<ResourceDescriptor>;
385
448
  createFromToken(options: CreateFromTokenOptions): Promise<{
386
- resourceId: string;
449
+ resourceId: ResourceId;
387
450
  }>;
388
451
  /** UI signal: user invoked the clone action from the resource-info panel. */
389
452
  clone(): void;
@@ -398,7 +461,7 @@ interface YieldNamespace$1 {
398
461
  * Event prefix: beckon:*
399
462
  */
400
463
  interface BeckonNamespace$1 {
401
- attention(annotationId: AnnotationId, resourceId: ResourceId): void;
464
+ attention(resourceId: ResourceId, annotationId: AnnotationId): void;
402
465
  hover(annotationId: AnnotationId | null): void;
403
466
  sparkle(annotationId: AnnotationId): void;
404
467
  }
@@ -420,7 +483,7 @@ interface JobNamespace$1 {
420
483
  timeout?: number;
421
484
  onProgress?: (status: JobStatusResponse$1) => void;
422
485
  }): Promise<JobStatusResponse$1>;
423
- cancel(jobId: JobId, type: string): Promise<void>;
486
+ cancelByType(jobType: 'annotation' | 'generation'): Promise<void>;
424
487
  /** UI signal: cancel all active jobs of a given type (e.g. "annotation"). */
425
488
  cancelRequest(jobType: 'annotation' | 'generation'): void;
426
489
  }
@@ -451,31 +514,21 @@ interface AdminNamespace$1 {
451
514
  oauthConfig(): Promise<OAuthConfigResponse$1>;
452
515
  healthCheck(): Promise<ResponseContent<paths['/api/health']['get']>>;
453
516
  status(): Promise<ResponseContent<paths['/api/status']['get']>>;
454
- backup(): Promise<Response>;
455
- restore(file: File, onProgress?: (event: {
456
- phase: string;
457
- message?: string;
458
- result?: Record<string, unknown>;
459
- }) => void): Promise<{
460
- phase: string;
461
- message?: string;
462
- result?: Record<string, unknown>;
463
- }>;
517
+ backup(): Promise<BackendDownload>;
518
+ /**
519
+ * Restore from a backup archive. Returns a `StreamObservable` that
520
+ * emits each `ProgressEvent` as the operation runs (`'started'`,
521
+ * `'parsing'`, `'importing'`, ..., `'complete'`). Subscribers see
522
+ * every step; awaiters get the final event via the PromiseLike sugar.
523
+ */
524
+ restore(file: File): StreamObservable<ProgressEvent>;
464
525
  exportKnowledgeBase(params?: {
465
526
  includeArchived?: boolean;
466
- }): Promise<Response>;
467
- importKnowledgeBase(file: File, onProgress?: (event: {
468
- phase: string;
469
- message?: string;
470
- result?: Record<string, unknown>;
471
- }) => void): Promise<{
472
- phase: string;
473
- message?: string;
474
- result?: Record<string, unknown>;
475
- }>;
527
+ }): Promise<BackendDownload>;
528
+ importKnowledgeBase(file: File): StreamObservable<ProgressEvent>;
476
529
  }
477
530
 
478
- type StoredEventResponse$1 = components['schemas']['StoredEventResponse'];
531
+ type StoredEventResponse = components['schemas']['StoredEventResponse'];
479
532
  type ResourceListFilters = {
480
533
  limit?: number;
481
534
  archived?: boolean;
@@ -519,7 +572,7 @@ declare class BrowseNamespace implements BrowseNamespace$1 {
519
572
  annotation(resourceId: ResourceId, annotationId: AnnotationId): CacheObservable<Annotation>;
520
573
  entityTypes(): CacheObservable<string[]>;
521
574
  referencedBy(resourceId: ResourceId): CacheObservable<ReferencedByEntry[]>;
522
- events(resourceId: ResourceId): CacheObservable<StoredEventResponse$1[]>;
575
+ events(resourceId: ResourceId): CacheObservable<StoredEventResponse[]>;
523
576
  resourceContent(resourceId: ResourceId): Promise<string>;
524
577
  resourceRepresentation(resourceId: ResourceId, options?: {
525
578
  accept?: string;
@@ -533,7 +586,7 @@ declare class BrowseNamespace implements BrowseNamespace$1 {
533
586
  stream: ReadableStream<Uint8Array>;
534
587
  contentType: string;
535
588
  }>;
536
- resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse$1[]>;
589
+ resourceEvents(resourceId: ResourceId): Promise<StoredEventResponse[]>;
537
590
  annotationHistory(resourceId: ResourceId, annotationId: AnnotationId): Promise<AnnotationHistoryResponse>;
538
591
  connections(_resourceId: ResourceId): Promise<GraphConnection[]>;
539
592
  backlinks(_resourceId: ResourceId): Promise<Annotation[]>;
@@ -582,12 +635,10 @@ declare class MarkNamespace implements MarkNamespace$1 {
582
635
  private readonly transport;
583
636
  private readonly bus;
584
637
  constructor(transport: ITransport, bus: EventBus);
585
- annotation(resourceId: ResourceId, input: CreateAnnotationInput): Promise<{
638
+ annotation(input: CreateAnnotationInput): Promise<{
586
639
  annotationId: AnnotationId;
587
640
  }>;
588
641
  delete(resourceId: ResourceId, annotationId: AnnotationId): Promise<void>;
589
- entityType(type: string): Promise<void>;
590
- entityTypes(types: string[]): Promise<void>;
591
642
  archive(resourceId: ResourceId): Promise<void>;
592
643
  unarchive(resourceId: ResourceId): Promise<void>;
593
644
  assist(resourceId: ResourceId, motivation: Motivation, options: MarkAssistOptions): StreamObservable<MarkAssistEvent>;
@@ -615,7 +666,7 @@ declare class GatherNamespace implements GatherNamespace$1 {
615
666
  private readonly transport;
616
667
  private readonly bus;
617
668
  constructor(transport: ITransport, bus: EventBus);
618
- annotation(annotationId: AnnotationId, resourceId: ResourceId, options?: {
669
+ annotation(resourceId: ResourceId, annotationId: AnnotationId, options?: {
619
670
  contextWindow?: number;
620
671
  }): StreamObservable<GatherAnnotationProgress>;
621
672
  resource(_resourceId: ResourceId, _options?: {
@@ -639,9 +690,7 @@ declare class YieldNamespace implements YieldNamespace$1 {
639
690
  private readonly bus;
640
691
  private readonly content;
641
692
  constructor(transport: ITransport, bus: EventBus, content: IContentTransport);
642
- resource(data: CreateResourceInput): Promise<{
643
- resourceId: string;
644
- }>;
693
+ resource(data: CreateResourceInput): UploadObservable;
645
694
  fromAnnotation(resourceId: ResourceId, annotationId: AnnotationId, options: GenerationOptions): StreamObservable<YieldGenerationEvent>;
646
695
  cloneToken(resourceId: ResourceId): Promise<{
647
696
  token: string;
@@ -649,7 +698,7 @@ declare class YieldNamespace implements YieldNamespace$1 {
649
698
  }>;
650
699
  fromToken(token: string): Promise<ResourceDescriptor>;
651
700
  createFromToken(options: CreateFromTokenOptions): Promise<{
652
- resourceId: string;
701
+ resourceId: ResourceId;
653
702
  }>;
654
703
  clone(): void;
655
704
  }
@@ -658,11 +707,38 @@ declare class BeckonNamespace implements BeckonNamespace$1 {
658
707
  private readonly transport;
659
708
  private readonly bus;
660
709
  constructor(transport: ITransport, bus: EventBus);
661
- attention(annotationId: AnnotationId, resourceId: ResourceId): void;
710
+ attention(resourceId: ResourceId, annotationId: AnnotationId): void;
662
711
  hover(annotationId: AnnotationId | null): void;
663
712
  sparkle(annotationId: AnnotationId): void;
664
713
  }
665
714
 
715
+ /**
716
+ * FrameNamespace — the eighth flow's surface.
717
+ *
718
+ * Frame operates on the KB's **schema layer** — the conceptual vocabulary
719
+ * the other seven flows are expressed in. Where yield/mark/match/bind/
720
+ * gather/browse/beckon act on content (resources, annotations, references,
721
+ * attention), Frame acts on what *kinds* of things exist: entity types,
722
+ * eventually tag schemas, relation/predicate types, ontology imports.
723
+ *
724
+ * The MVP owns a single primitive — entity-type vocabulary writes on the
725
+ * `frame:add-entity-type` channel. See `docs/protocol/flows/FRAME.md`
726
+ * for the per-flow contract.
727
+ *
728
+ * Live reads of the entity-type vocabulary stay on Browse
729
+ * (`browse.entityTypes()` is a `CacheObservable<string[]>`). Frame owns
730
+ * writes; Browse owns reads. The asymmetry is intentional — re-implementing
731
+ * Browse's cache primitives on Frame for a single read would duplicate
732
+ * machinery without benefit.
733
+ */
734
+
735
+ declare class FrameNamespace implements FrameNamespace$1 {
736
+ private readonly transport;
737
+ constructor(transport: ITransport);
738
+ addEntityType(type: string): Promise<void>;
739
+ addEntityTypes(types: string[]): Promise<void>;
740
+ }
741
+
666
742
  type JobStatusResponse = components['schemas']['JobStatusResponse'];
667
743
  declare class JobNamespace implements JobNamespace$1 {
668
744
  private readonly transport;
@@ -686,19 +762,19 @@ declare class JobNamespace implements JobNamespace$1 {
686
762
  timeout?: number;
687
763
  onProgress?: (status: JobStatusResponse) => void;
688
764
  }): Promise<JobStatusResponse>;
689
- cancel(_jobId: JobId, type: string): Promise<void>;
765
+ cancelByType(jobType: 'annotation' | 'generation'): Promise<void>;
690
766
  cancelRequest(jobType: 'annotation' | 'generation'): void;
691
767
  }
692
768
 
693
769
  /**
694
- * AuthNamespace — authentication. Pure wire, no bus.
770
+ * AuthNamespace — authentication. Backend ops only; no bus.
695
771
  */
696
772
 
697
773
  type AuthResponse = components['schemas']['AuthResponse'];
698
774
  type TokenRefreshResponse = components['schemas']['TokenRefreshResponse'];
699
775
  declare class AuthNamespace implements AuthNamespace$1 {
700
- private readonly transport;
701
- constructor(transport: ITransport);
776
+ private readonly backend;
777
+ constructor(backend: IBackendOperations);
702
778
  password(emailStr: string, passwordStr: string): Promise<AuthResponse>;
703
779
  google(credential: string): Promise<AuthResponse>;
704
780
  refresh(token: string): Promise<TokenRefreshResponse>;
@@ -714,42 +790,26 @@ declare class AuthNamespace implements AuthNamespace$1 {
714
790
  }
715
791
 
716
792
  /**
717
- * AdminNamespace — administration. Pure wire, no bus.
793
+ * AdminNamespace — administration. Backend ops only; no bus.
718
794
  */
719
795
 
720
796
  type AdminUserStatsResponse = components['schemas']['AdminUserStatsResponse'];
721
797
  type OAuthConfigResponse = components['schemas']['OAuthConfigResponse'];
722
798
  declare class AdminNamespace implements AdminNamespace$1 {
723
- private readonly transport;
724
- constructor(transport: ITransport);
799
+ private readonly backend;
800
+ constructor(backend: IBackendOperations);
725
801
  users(): Promise<User[]>;
726
802
  userStats(): Promise<AdminUserStatsResponse>;
727
803
  updateUser(userId: UserDID, data: RequestContent<paths['/api/admin/users/{id}']['patch']>): Promise<User>;
728
804
  oauthConfig(): Promise<OAuthConfigResponse>;
729
805
  healthCheck(): Promise<ResponseContent<paths['/api/health']['get']>>;
730
806
  status(): Promise<ResponseContent<paths['/api/status']['get']>>;
731
- backup(): Promise<Response>;
732
- restore(file: File, onProgress?: (event: {
733
- phase: string;
734
- message?: string;
735
- result?: Record<string, unknown>;
736
- }) => void): Promise<{
737
- phase: string;
738
- message?: string;
739
- result?: Record<string, unknown>;
740
- }>;
807
+ backup(): Promise<BackendDownload>;
808
+ restore(file: File): StreamObservable<ProgressEvent>;
741
809
  exportKnowledgeBase(params?: {
742
810
  includeArchived?: boolean;
743
- }): Promise<Response>;
744
- importKnowledgeBase(file: File, onProgress?: (event: {
745
- phase: string;
746
- message?: string;
747
- result?: Record<string, unknown>;
748
- }) => void): Promise<{
749
- phase: string;
750
- message?: string;
751
- result?: Record<string, unknown>;
752
- }>;
811
+ }): Promise<BackendDownload>;
812
+ importKnowledgeBase(file: File): StreamObservable<ProgressEvent>;
753
813
  }
754
814
 
755
815
  declare class SemiontClient {
@@ -770,6 +830,7 @@ declare class SemiontClient {
770
830
  */
771
831
  readonly bus: EventBus;
772
832
  readonly baseUrl: BaseUrl;
833
+ readonly frame: FrameNamespace;
773
834
  readonly browse: BrowseNamespace;
774
835
  readonly mark: MarkNamespace;
775
836
  readonly bind: BindNamespace;
@@ -778,8 +839,8 @@ declare class SemiontClient {
778
839
  readonly yield: YieldNamespace;
779
840
  readonly beckon: BeckonNamespace;
780
841
  readonly job: JobNamespace;
781
- readonly auth: AuthNamespace;
782
- readonly admin: AdminNamespace;
842
+ readonly auth: AuthNamespace | undefined;
843
+ readonly admin: AdminNamespace | undefined;
783
844
  /**
784
845
  * The client *owns* its bus. The constructor creates a fresh `EventBus`
785
846
  * and hands it to the transport via `transport.bridgeInto(this.bus)`.
@@ -792,8 +853,14 @@ declare class SemiontClient {
792
853
  * Callers do not pass a bus in. If they need to interact with the bus
793
854
  * (e.g. for tests or to subscribe to arbitrary channels), they read it
794
855
  * back via `client.bus`.
856
+ *
857
+ * `backend` is optional. When provided, the `auth` and `admin`
858
+ * namespaces are constructed against it; when omitted, they're
859
+ * `undefined`. For HTTP setups this is conventionally the same
860
+ * `HttpTransport` instance that's also passed as `transport` (HTTP
861
+ * implements both `ITransport` and `IBackendOperations`).
795
862
  */
796
- constructor(transport: ITransport, content: IContentTransport);
863
+ constructor(transport: ITransport, content: IContentTransport, backend?: IBackendOperations);
797
864
  /** Transport-level connection state. HTTP reflects SSE health; local is always 'connected'. */
798
865
  get state$(): rxjs.Observable<_semiont_core.ConnectionState>;
799
866
  subscribeToResource(resourceId: ResourceId): () => void;
@@ -807,9 +874,10 @@ declare class SemiontClient {
807
874
  * Use this for one-shot scripts, CLI commands, or any consumer that
808
875
  * doesn't need to drive the token from outside (no manual refresh,
809
876
  * no cross-tab sync). For long-running scripts that need refresh,
810
- * use `SemiontSession.fromHttp(...)` instead it owns the same
811
- * transport/client wiring plus the proactive-refresh + storage
812
- * machinery.
877
+ * use `SemiontSession.fromHttp(...)` (with a token already on hand)
878
+ * or `SemiontSession.signInHttp(...)` (credentials-first) instead
879
+ * either owns the same transport/client wiring plus the
880
+ * proactive-refresh + storage machinery.
813
881
  *
814
882
  * Strings are accepted for `baseUrl` and `token`; they are branded
815
883
  * via `baseUrl()` / `accessToken()` from `@semiont/core` automatically.
@@ -823,9 +891,9 @@ declare class SemiontClient {
823
891
  }): SemiontClient;
824
892
  /**
825
893
  * Async factory for the credentials-first script case. Builds a
826
- * transient transport, calls `auth.password(email, password)` to
827
- * acquire an access token, and returns the wired client with the
828
- * token populated.
894
+ * transient HTTP transport, calls `auth.password(email, password)`
895
+ * to acquire an access token, and returns the wired client with
896
+ * the token populated.
829
897
  *
830
898
  * This is the right entry point for skills, CLI scripts, and any
831
899
  * consumer that starts with email + password rather than a JWT
@@ -834,13 +902,19 @@ declare class SemiontClient {
834
902
  * `fromHttp({ baseUrl, token })` instead.
835
903
  *
836
904
  * For long-running scripts that need refresh, use
837
- * `SemiontSession.signIn(...)` — same credentials shape, plus the
838
- * session machinery for proactive refresh and persistence.
905
+ * `SemiontSession.signInHttp(...)` — same credentials shape, plus
906
+ * the session machinery for proactive refresh and persistence.
907
+ *
908
+ * Named `signInHttp` because email+password authentication is
909
+ * inherently an HTTP-shaped operation in the current backend; an
910
+ * in-process `LocalTransport` doesn't have a credentials login
911
+ * path. Non-HTTP transports construct the client directly from
912
+ * their package's transport instance.
839
913
  *
840
914
  * Throws if authentication fails. The transient client is disposed
841
915
  * before the throw, so no resources leak on failure.
842
916
  */
843
- static signIn(opts: {
917
+ static signInHttp(opts: {
844
918
  baseUrl: BaseUrl | string;
845
919
  email: string;
846
920
  password: string;
@@ -864,76 +938,49 @@ interface BusRequestPrimitive {
864
938
  declare function busRequest<TResult>(bus: BusRequestPrimitive, emitChannel: string, payload: Record<string, unknown>, resultChannel: string, failureChannel: string, timeoutMs?: number): Promise<TResult>;
865
939
 
866
940
  /**
867
- * RxJS-native read-through cache primitive.
868
- *
869
- * Behavioral contract: packages/sdk/docs/CACHE-SEMANTICS.md (B1–B13).
870
- *
871
- * Framework-agnostic: no React, no dependency on any namespace. Used by
872
- * `BrowseNamespace` to back its per-key stores, but equally usable from
873
- * CLI, MCP, or worker code.
874
- *
875
- * Shape:
876
- * - `observe(key)`: returns an Observable<V | undefined> that triggers
877
- * a fetch on first subscription for a missing key, dedup-joins any
878
- * concurrent fetch, and emits the stored value thereafter.
879
- * - `invalidate(key)`: stale-while-revalidate — keeps the current
880
- * value visible to observers, clears the in-flight guard, starts a
881
- * fresh fetch. If the previous fetch was orphaned (SSE torn down,
882
- * response lost), this is how the cache recovers.
883
- * - `remove(key)`: drops the cache entry entirely. Used for entity
884
- * deletions (B13a). No refetch.
885
- * - `set(key, value)`: writes through without a fetch. Used when a
886
- * bus event carries the new value inline (B13b).
887
- * - `invalidateAll()`: per-key SWR refetch of every currently-cached
888
- * entry. Used by gap-detection paths.
889
- * - `dispose()`: completes the store so observers unsubscribe.
890
- *
891
- * What's deliberately out:
892
- * - No subscriber ref-counting / GC of unobserved keys. The per-key
893
- * observable memo grows with the set of observed keys for the cache's
894
- * lifetime (B11). Acceptable given cache lifetime == client lifetime.
895
- * - No TTL / cacheTime. Entries are evicted only by explicit remove.
896
- * - No retry / backoff. A failing fetch leaves the cache unchanged
897
- * (B6); the caller drives retry via invalidate.
898
- */
899
-
900
- interface Cache<K, V> {
901
- /** Observable stream of the value at `key`. Triggers a fetch if not cached. */
902
- observe(key: K): Observable<V | undefined>;
903
- /** Synchronous snapshot of the current value, without triggering a fetch. */
904
- get(key: K): V | undefined;
905
- /** Iterator of currently-cached keys. For invalidateAll and diagnostics. */
906
- keys(): K[];
907
- /**
908
- * Mark the entry stale and refetch. Keeps the previous value visible
909
- * to observers during the refetch (stale-while-revalidate).
910
- */
911
- invalidate(key: K): void;
912
- /** Drop the entry from the cache. No refetch. */
913
- remove(key: K): void;
914
- /** Write-through: set the value directly without a fetch. */
915
- set(key: K, value: V): void;
916
- /** Per-key SWR refetch of every currently-cached entry. */
917
- invalidateAll(): void;
918
- /** Release the underlying subject. Observers complete. */
919
- dispose(): void;
920
- }
921
- declare function createCache<K, V>(fetchFn: (key: K) => Promise<V>): Cache<K, V>;
922
-
923
- /**
924
- * KnowledgeBase — a connection to a Semiont backend instance.
941
+ * KnowledgeBase a connection to a Semiont knowledge system.
942
+ *
943
+ * The KB type itself is uniform. The transport-shape variation lives in
944
+ * the nested `endpoint` field, which is a discriminated union:
925
945
  *
926
- * Each KB has its own JWT, its own API base URL, and its own session.
927
- * The user is "authenticated against KB X" — never globally authenticated.
946
+ * - `endpoint.kind === 'http'` — a remote backend reached over HTTP+SSE.
947
+ * Carries `host`/`port`/`protocol`.
948
+ * - `endpoint.kind === 'local'` — an in-process knowledge system reached
949
+ * via `LocalTransport` from
950
+ * `@semiont/make-meaning`. Carries an
951
+ * opaque `kbId` identifying the local
952
+ * instance to the host process.
953
+ *
954
+ * Code that doesn't know how to make a transport (`SemiontSession`,
955
+ * `SemiontBrowser`, the frontend KB list UI) treats `KnowledgeBase` as
956
+ * uniform and never inspects `endpoint`. Code that *does* construct
957
+ * transports (the transport-factory passed to `SemiontBrowser`,
958
+ * `kbBackendUrl` for HTTP URL construction) inspects `endpoint.kind`
959
+ * and dispatches.
960
+ *
961
+ * Each KB has its own session, its own credentials (where applicable),
962
+ * and its own JWT (HTTP only). The user is "authenticated against KB X" —
963
+ * never globally authenticated.
928
964
  */
965
+ /** Fields shared by every KB regardless of endpoint kind. */
929
966
  interface KnowledgeBase {
930
967
  id: string;
931
968
  label: string;
969
+ email: string;
970
+ gitBranch?: string;
971
+ endpoint: KbEndpoint;
972
+ }
973
+ type KbEndpoint = HttpEndpoint | LocalEndpoint;
974
+ interface HttpEndpoint {
975
+ kind: 'http';
932
976
  host: string;
933
977
  port: number;
934
978
  protocol: 'http' | 'https';
935
- email: string;
936
- gitBranch?: string;
979
+ }
980
+ interface LocalEndpoint {
981
+ kind: 'local';
982
+ /** Opaque identifier for the in-process KB instance the host has loaded. */
983
+ kbId: string;
937
984
  }
938
985
  /**
939
986
  * Input shape for adding a new KB. The id is generated by the provider.
@@ -944,6 +991,46 @@ type NewKnowledgeBase = Omit<KnowledgeBase, 'id'>;
944
991
  * presence and validity of the JWT in session storage.
945
992
  */
946
993
  type KbSessionStatus = 'authenticated' | 'expired' | 'signed-out' | 'unreachable';
994
+ /**
995
+ * Construct a `KnowledgeBase` for an HTTP-backed Semiont backend without
996
+ * spelling out the nested `endpoint` literal. Convenience for tests,
997
+ * worker bootstraps, and one-off scripts.
998
+ *
999
+ * ```ts
1000
+ * const kb = httpKb({
1001
+ * id: 'my-watcher',
1002
+ * label: 'My Watcher',
1003
+ * email: 'me@example.com',
1004
+ * host: 'localhost',
1005
+ * port: 4000,
1006
+ * protocol: 'http',
1007
+ * });
1008
+ * ```
1009
+ *
1010
+ * Equivalent to:
1011
+ *
1012
+ * ```ts
1013
+ * const kb: KnowledgeBase = {
1014
+ * id, label, email,
1015
+ * endpoint: { kind: 'http', host, port, protocol },
1016
+ * };
1017
+ * ```
1018
+ *
1019
+ * UI hosts that have a structured form (host / port / protocol pickers)
1020
+ * already construct the literal directly — they don't need this helper.
1021
+ * Local-endpoint KBs construct the literal directly too; the local
1022
+ * endpoint shape (`{ kind: 'local', kbId }`) is one line and doesn't
1023
+ * earn a helper.
1024
+ */
1025
+ declare function httpKb(opts: {
1026
+ id: string;
1027
+ label: string;
1028
+ email: string;
1029
+ host: string;
1030
+ port: number;
1031
+ protocol: 'http' | 'https';
1032
+ gitBranch?: string;
1033
+ }): KnowledgeBase;
947
1034
 
948
1035
  /**
949
1036
  * Session-level error surface. Emitted on `SemiontBrowser.error$` for
@@ -1012,7 +1099,7 @@ declare class InMemorySessionStorage implements SessionStorage {
1012
1099
  *
1013
1100
  * Headless by design. Runs in browsers, CLIs, workers, and tests.
1014
1101
  * UI-specific state (session-expired/permission-denied modals) lives
1015
- * in `FrontendSessionSignals`, which wraps a session — the session
1102
+ * in `SessionSignals`, which wraps a session — the session
1016
1103
  * itself has no modal observables, no user-facing notifications.
1017
1104
  *
1018
1105
  * Auth is parameterized via callbacks passed at construction:
@@ -1028,9 +1115,9 @@ declare class InMemorySessionStorage implements SessionStorage {
1028
1115
  * worker omits this (service principals have no user record).
1029
1116
  *
1030
1117
  * - `onAuthFailed(message)` — optional. Invoked when refresh
1031
- * terminally fails (expired token, no recovery possible). The
1032
- * frontend wires this to `FrontendSessionSignals.notifySessionExpired`
1033
- * so the modal surfaces; headless consumers typically just log.
1118
+ * terminally fails (expired token, no recovery possible). UI hosts
1119
+ * typically wire this to `SessionSignals.notifySessionExpired` so a
1120
+ * modal surfaces; headless consumers typically just log.
1034
1121
  *
1035
1122
  * Persistence goes through a `SessionStorage` adapter provided at
1036
1123
  * construction — the session never touches `localStorage` or `window`
@@ -1082,6 +1169,18 @@ declare class SemiontSession {
1082
1169
  readonly token$: BehaviorSubject<AccessToken | null>;
1083
1170
  readonly user$: BehaviorSubject<UserInfo | null>;
1084
1171
  readonly streamState$: Observable<ConnectionState>;
1172
+ /**
1173
+ * Stream of `SemiontError` instances surfaced by the underlying transport
1174
+ * just before they're thrown to the caller. For `HttpTransport` this is
1175
+ * an `APIError` (status-coded); other transports emit their own subclass.
1176
+ * Surfaced here so a host layer (e.g. `SemiontBrowser`) can route by
1177
+ * `err.code` to global notifications without every call site handling
1178
+ * errors itself. Headless consumers can subscribe for logging.
1179
+ *
1180
+ * Re-published from `client.transport.errors$` per the `ITransport`
1181
+ * contract — the session is purely a passthrough.
1182
+ */
1183
+ readonly errors$: Observable<SemiontError>;
1085
1184
  /** Resolves after the initial validation round-trip completes (success or failure). */
1086
1185
  readonly ready: Promise<void>;
1087
1186
  private readonly storage;
@@ -1156,11 +1255,11 @@ declare class SemiontSession {
1156
1255
  }): SemiontSession;
1157
1256
  /**
1158
1257
  * Async factory for the credentials-first long-running script case.
1159
- * Builds the transport stack, calls `auth.password(email, password)`
1160
- * to acquire access + refresh tokens, persists them via the storage
1161
- * adapter, wires a default `refresh` callback that exchanges the
1162
- * refresh token via `auth.refresh(...)`, and returns the ready
1163
- * session.
1258
+ * Builds the HTTP transport stack, calls `auth.password(email,
1259
+ * password)` to acquire access + refresh tokens, persists them via
1260
+ * the storage adapter, wires a default `refresh` callback that
1261
+ * exchanges the refresh token via `auth.refresh(...)`, and returns
1262
+ * the ready session.
1164
1263
  *
1165
1264
  * The consumer-supplied `refresh` callback becomes optional — only
1166
1265
  * needed for non-standard refresh flows (worker-pool shared secret,
@@ -1173,10 +1272,16 @@ declare class SemiontSession {
1173
1272
  * trampling each other's tokens. The factory does not synthesize a
1174
1273
  * default; the consumer makes the choice.
1175
1274
  *
1275
+ * Named `signInHttp` because email+password authentication is
1276
+ * inherently an HTTP-shaped operation in the current backend; an
1277
+ * in-process `LocalTransport` doesn't have a credentials login
1278
+ * path. Non-HTTP transports construct the session directly from
1279
+ * their package's transport instance.
1280
+ *
1176
1281
  * Throws on auth failure with no resources leaked. On success, the
1177
1282
  * returned session's `ready` promise has already resolved.
1178
1283
  */
1179
- static signIn(opts: {
1284
+ static signInHttp(opts: {
1180
1285
  kb: KnowledgeBase;
1181
1286
  storage: SessionStorage;
1182
1287
  baseUrl: BaseUrl | string;
@@ -1211,36 +1316,33 @@ interface OpenResource {
1211
1316
  }
1212
1317
 
1213
1318
  /**
1214
- * FrontendSessionSignalsmodal state that belongs to the UI, not
1215
- * the session itself.
1319
+ * SessionSignalsUI-facing notification state that belongs to the host
1320
+ * surface, not to the session itself.
1216
1321
  *
1217
1322
  * `SemiontSession` is a headless per-backend client + token + user
1218
1323
  * holder. It can run in any process: browser, worker, CLI, test. But
1219
- * the session-expired / permission-denied *modals* only make sense
1220
- * in a UI context. Keeping those observables on `SemiontSession`
1221
- * meant workers and CLIs carried four dead BehaviorSubjects that
1222
- * nothing would ever fire.
1324
+ * the session-expired / permission-denied *notifications* are inherently
1325
+ * a UI-host concern. Keeping those observables on `SemiontSession` meant
1326
+ * workers and CLIs carried four dead BehaviorSubjects that nothing would
1327
+ * ever fire.
1223
1328
  *
1224
- * `FrontendSessionSignals` owns the modal state and has no hard
1225
- * reference to a session. `SemiontBrowser` constructs one alongside
1226
- * every frontend session and wires:
1329
+ * `SessionSignals` owns the notification state and has no hard reference
1330
+ * to a session. A UI host (e.g. `SemiontBrowser`) constructs one alongside
1331
+ * every active session and wires:
1227
1332
  *
1228
1333
  * - `session.onAuthFailed` → `signals.notifySessionExpired` so
1229
- * proactive-refresh failures surface as modals
1230
- * - `notify` module handlers → the active signals' methods so
1231
- * external callers (e.g. React Query's QueryCache.onError) can
1232
- * trigger the modals without touching the session
1334
+ * proactive-refresh failures surface as a notification
1233
1335
  *
1234
- * React consumers that need modal state subscribe here; consumers
1235
- * that need bus/HTTP access continue to subscribe to the session.
1336
+ * UI consumers that need to render modal/banner state subscribe here;
1337
+ * consumers that need bus/HTTP access continue to subscribe to the session.
1236
1338
  *
1237
1339
  * Session auth-state cleanup (clearing token, clearing storage) is
1238
1340
  * the session's own responsibility inside `refresh()` — by the time
1239
1341
  * `notifySessionExpired` runs, the session has already torn down.
1240
- * Signals only surfaces the modal.
1342
+ * Signals only surfaces the notification.
1241
1343
  */
1242
1344
 
1243
- declare class FrontendSessionSignals {
1345
+ declare class SessionSignals {
1244
1346
  readonly sessionExpiredAt$: BehaviorSubject<number | null>;
1245
1347
  readonly sessionExpiredMessage$: BehaviorSubject<string | null>;
1246
1348
  readonly permissionDeniedAt$: BehaviorSubject<number | null>;
@@ -1253,14 +1355,46 @@ declare class FrontendSessionSignals {
1253
1355
  dispose(): void;
1254
1356
  }
1255
1357
 
1358
+ /**
1359
+ * SessionFactory — the injection point that lets `SemiontBrowser` stay
1360
+ * transport-agnostic.
1361
+ *
1362
+ * The browser knows how to manage the *lifecycle* of an active session
1363
+ * (track it in `activeSession$`, dispose on KB switch, serialize
1364
+ * overlapping activations) but does not know how to *construct* one —
1365
+ * because that's where transport choice lives. The construction step
1366
+ * is parameterized via this factory.
1367
+ *
1368
+ * The HTTP factory is provided by `createHttpSessionFactory`. A
1369
+ * future in-process variant from `@semiont/make-meaning` would expose
1370
+ * its own factory.
1371
+ */
1372
+
1373
+ interface SessionFactoryOptions {
1374
+ /** The KB the session is being constructed for. */
1375
+ kb: KnowledgeBase;
1376
+ /** Persistence adapter — same one the browser uses. */
1377
+ storage: SessionStorage;
1378
+ /** Modal-signal sink for auth-failed / permission-denied notifications. */
1379
+ signals: SessionSignals;
1380
+ /** Receives session-level errors (auth-failed, refresh-exhausted, ...). */
1381
+ onError: (err: SemiontSessionError) => void;
1382
+ }
1383
+ type SessionFactory = (opts: SessionFactoryOptions) => SemiontSession;
1384
+
1256
1385
  /**
1257
1386
  * SemiontBrowser — top-level app-facing container for non-KB state.
1258
1387
  *
1259
1388
  * Holds the list of configured KBs, the active KB selection, the active
1260
1389
  * SemiontSession, the identity token, the open-resources list, and a
1261
- * session-level error stream. Module-scoped singleton survives every
1262
- * React re-render, remount, and route change. `SemiontProvider` hands
1263
- * the singleton to the React tree; `useSemiont()` returns it.
1390
+ * session-level error stream. Held as a process-wide instance for the
1391
+ * host's lifetime see `getBrowser()` in `registry.ts` for the canonical
1392
+ * accessor.
1393
+ *
1394
+ * Transport-agnostic: the browser orchestrates session *lifecycle* but
1395
+ * delegates session *construction* to a `SessionFactory` injected at
1396
+ * construction. HTTP-backed apps pass `createHttpSessionFactory()` from
1397
+ * `@semiont/sdk`; in-process apps pass their own factory.
1264
1398
  *
1265
1399
  * Persistence goes through a `SessionStorage` adapter provided at
1266
1400
  * construction — the browser never touches `localStorage` or `window`
@@ -1270,6 +1404,14 @@ declare class FrontendSessionSignals {
1270
1404
  interface SemiontBrowserConfig {
1271
1405
  /** Persistence adapter. The browser reads/writes all persisted state via this. */
1272
1406
  storage: SessionStorage;
1407
+ /**
1408
+ * Builds a `SemiontSession` for a KB. The browser is transport-
1409
+ * agnostic — every HTTP-vs-local construction concern lives in the
1410
+ * factory. HTTP-backed apps pass `createHttpSessionFactory()` from
1411
+ * `@semiont/sdk`; a future in-process variant from `@semiont/make-meaning`
1412
+ * would expose its own factory.
1413
+ */
1414
+ sessionFactory: SessionFactory;
1273
1415
  }
1274
1416
  declare class SemiontBrowser {
1275
1417
  readonly kbs$: BehaviorSubject<KnowledgeBase[]>;
@@ -1281,9 +1423,9 @@ declare class SemiontBrowser {
1281
1423
  * non-null when `activeSession$` is non-null, always null when it
1282
1424
  * is. Extracted from the session itself so headless sessions
1283
1425
  * (workers, CLIs, tests) don't carry dead modal observables.
1284
- * See [FrontendSessionSignals](./frontend-session-signals.ts).
1426
+ * See [SessionSignals](./session-signals.ts).
1285
1427
  */
1286
- readonly activeSignals$: BehaviorSubject<FrontendSessionSignals | null>;
1428
+ readonly activeSignals$: BehaviorSubject<SessionSignals | null>;
1287
1429
  /**
1288
1430
  * True while a session is actively being constructed (setActiveKb /
1289
1431
  * signIn in flight, awaiting `session.ready`). Distinguishes the
@@ -1297,6 +1439,7 @@ declare class SemiontBrowser {
1297
1439
  readonly error$: Subject<SemiontSessionError>;
1298
1440
  readonly identityToken$: BehaviorSubject<string | null>;
1299
1441
  private readonly storage;
1442
+ private readonly sessionFactory;
1300
1443
  /**
1301
1444
  * App-scoped EventBus. Hosts UI-shell events that must work regardless
1302
1445
  * of whether a KB session is active: panel toggles, sidebar state,
@@ -1305,18 +1448,9 @@ declare class SemiontBrowser {
1305
1448
  * (mark:*, beckon:*, gather:*, match:*, bind:*, yield:*, browse:click).
1306
1449
  */
1307
1450
  private readonly eventBus;
1308
- private unregisterNotify;
1309
1451
  private unsubscribeStorage;
1310
1452
  private disposed;
1311
1453
  private activating;
1312
- /**
1313
- * Per-KB in-flight refresh dedup. Simultaneous 401s for the same
1314
- * KB converge on a single `/api/tokens/refresh` network call.
1315
- * Was previously module-scoped in `refresh.ts`; moved here when
1316
- * that file was deleted — SemiontBrowser is a singleton so the
1317
- * scoping is equivalent.
1318
- */
1319
- private readonly inFlightRefreshes;
1320
1454
  constructor(config: SemiontBrowserConfig);
1321
1455
  /** Emit an event on the browser's app-scoped bus. */
1322
1456
  emit<K extends keyof EventMap>(channel: K, payload: EventMap[K]): void;
@@ -1325,14 +1459,25 @@ declare class SemiontBrowser {
1325
1459
  /** Read-only observable for an app-scoped channel. */
1326
1460
  stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]>;
1327
1461
  /**
1328
- * Set the app-level identity token (from NextAuth's useSession).
1329
- * Called at the root layout via a single `useEffect`. No other site
1330
- * in the codebase should call this.
1462
+ * Set the app-level identity token. Sourced from whatever the host
1463
+ * environment uses for OAuth sessions (e.g. NextAuth in a browser app).
1464
+ * Should be called once from the host's startup-and-on-change site;
1465
+ * no other code should write to this slot.
1331
1466
  */
1332
1467
  setIdentityToken(token: string | null): void;
1333
1468
  addKb(input: NewKnowledgeBase, access: string, refresh: string): KnowledgeBase;
1334
1469
  removeKb(id: string): void;
1335
- updateKb(id: string, updates: Partial<KnowledgeBase>): void;
1470
+ /**
1471
+ * Patch a KB in the list. Restricted to the common, endpoint-agnostic
1472
+ * fields (`label`, `email`, `gitBranch`) — the `endpoint` shape isn't
1473
+ * editable in place; remove and re-add to change the connection
1474
+ * target.
1475
+ */
1476
+ updateKb(id: string, updates: {
1477
+ label?: string;
1478
+ email?: string;
1479
+ gitBranch?: string;
1480
+ }): void;
1336
1481
  /**
1337
1482
  * Read the locally-stored credential status for a KB. Pure / synchronous —
1338
1483
  * does not subscribe to context changes. Used by KB-list UI to color status
@@ -1367,46 +1512,45 @@ declare class SemiontBrowser {
1367
1512
  removeOpenResource(id: string): void;
1368
1513
  updateOpenResourceName(id: string, name: string): void;
1369
1514
  reorderOpenResources(oldIndex: number, newIndex: number): void;
1370
- /**
1371
- * Refresh the active KB's access token. Returns the new token on
1372
- * success, null on failure. Concurrent calls for the same KB
1373
- * dedupe through `inFlightRefreshes`, so simultaneous 401s trigger
1374
- * only one `/api/tokens/refresh` round trip.
1375
- *
1376
- * Uses a throwaway `SemiontClient` with no `tokenRefresher` —
1377
- * a refresh call returning 401 would otherwise re-enter this
1378
- * function infinitely.
1379
- */
1380
- private performRefresh;
1381
- /**
1382
- * Validate an access token by calling `auth.me` on a throwaway
1383
- * client. The session uses this once at startup to populate
1384
- * `user$`; 401 triggers a refresh-then-retry inside the session.
1385
- *
1386
- * The throwaway transport is seeded with the specific token to
1387
- * validate so the request actually carries it (HttpTransport
1388
- * sources `Authorization` from its `token$`).
1389
- */
1390
- private performValidate;
1391
1515
  dispose(): Promise<void>;
1392
1516
  }
1393
1517
 
1394
1518
  /**
1395
- * Module-scoped singleton for SemiontBrowser. Constructed lazily on first
1396
- * `getBrowser()` call, survives every React re-render, remount, and route
1397
- * change.
1398
- *
1399
- * The caller provides a `SessionStorage` implementation there is no
1400
- * default here because storage-backend selection is environment-specific
1401
- * (browsers use `WebBrowserStorage`, CLI uses a filesystem adapter,
1402
- * tests use `InMemorySessionStorage`). The first call to `getBrowser`
1403
- * wins; subsequent calls return the cached instance regardless of the
1404
- * storage passed.
1519
+ * createHttpSessionFactory the default `SessionFactory` for HTTP-backed
1520
+ * KBs. Owns every HTTP-specific construction concern that used to live in
1521
+ * `SemiontBrowser`: building `HttpTransport`/`HttpContentTransport`,
1522
+ * wiring the `tokenRefresher` callback, deduplicating concurrent 401
1523
+ * refresh round trips, and invoking the auth endpoints for token refresh
1524
+ * and user-validate.
1525
+ *
1526
+ * Returned as a closure so a single `inFlightRefreshes` map is shared
1527
+ * across every session this factory builds the dedup is meaningful
1528
+ * across concurrent session reactivations for the same KB id.
1529
+ */
1530
+
1531
+ declare function createHttpSessionFactory(): SessionFactory;
1532
+
1533
+ /**
1534
+ * Process-wide accessor for `SemiontBrowser`. Constructed lazily on the
1535
+ * first `getBrowser()` call and held for the host's lifetime — a single
1536
+ * instance owns the KB list, identity token, and active-session state,
1537
+ * so callers throughout the host get the same view of "which KB am I
1538
+ * talking to right now" regardless of where they pick it up.
1539
+ *
1540
+ * The caller provides a `SessionStorage` implementation and a
1541
+ * `SessionFactory` — both are environment-specific (browsers use
1542
+ * `WebBrowserStorage` + `createHttpSessionFactory()`, CLI/embedded
1543
+ * hosts use a filesystem adapter and possibly a local-process
1544
+ * factory, tests use `InMemorySessionStorage` and stubs). The first
1545
+ * call to `getBrowser` wins; subsequent calls return the cached
1546
+ * instance regardless of the options passed.
1405
1547
  */
1406
1548
 
1407
1549
  interface GetBrowserOptions {
1408
1550
  /** Persistence adapter used to construct the singleton on first call. */
1409
1551
  storage: SessionStorage;
1552
+ /** Session factory used to build a `SemiontSession` per active KB. */
1553
+ sessionFactory: SessionFactory;
1410
1554
  }
1411
1555
  declare function getBrowser(options: GetBrowserOptions): SemiontBrowser;
1412
1556
 
@@ -1433,16 +1577,45 @@ interface StoredSession {
1433
1577
  declare function setStoredSession(storage: SessionStorage, kbId: string, session: StoredSession): void;
1434
1578
  declare function defaultProtocol(host: string): 'http' | 'https';
1435
1579
  declare function isValidHostname(host: string): boolean;
1436
- declare function kbBackendUrl(kb: KnowledgeBase): string;
1437
-
1438
- declare function notifySessionExpired(message?: string): void;
1439
- declare function notifyPermissionDenied(message?: string): void;
1580
+ /**
1581
+ * Build the wire URL for an HTTP KB endpoint. HTTP-shaped helper —
1582
+ * lives next to the KB list machinery because the frontend Panel needs
1583
+ * it for the auth round-trip when adding a KB. Code that holds a
1584
+ * uniform `KnowledgeBase` should not call this; it should hand the KB
1585
+ * to a transport factory and let the factory inspect `endpoint.kind`.
1586
+ */
1587
+ declare function kbBackendUrl(endpoint: HttpEndpoint): string;
1440
1588
 
1441
- interface ViewModel {
1589
+ /**
1590
+ * Marker for the state-unit pattern: a stateful, lifecycled object with an
1591
+ * RxJS-shaped public surface, constructed by a factory function
1592
+ * (`createFooStateUnit`), with internal state held in a closure.
1593
+ *
1594
+ * The structural contract is `dispose()` — the rest of the pattern
1595
+ * (closure-based identity, Observable public surface, internal Subjects
1596
+ * exposed as `.asObservable()` views, no leaked subscriptions, composition
1597
+ * by parameter rather than ownership) is convention enforced by review,
1598
+ * not the type system.
1599
+ *
1600
+ * See `packages/sdk/docs/STATE-UNITS.md` for the full axioms and rationale.
1601
+ */
1602
+ interface StateUnit {
1603
+ /**
1604
+ * Idempotent, total teardown. Completes every Subject the unit owns,
1605
+ * unsubscribes every internal subscription, releases timers / abort
1606
+ * controllers / network handles. Safe to call multiple times — the
1607
+ * second call is a no-op.
1608
+ */
1442
1609
  dispose(): void;
1443
1610
  }
1611
+ /**
1612
+ * Compose multiple disposers into a single `dispose()` call. Accepts either
1613
+ * a `StateUnit` (whose `dispose()` will be invoked) or a plain teardown
1614
+ * function. The returned object is itself disposable; call its `dispose()`
1615
+ * once to tear down everything that was added.
1616
+ */
1444
1617
  declare function createDisposer(): {
1445
- add(vm: ViewModel | (() => void)): void;
1618
+ add(item: StateUnit | (() => void)): void;
1446
1619
  dispose(): void;
1447
1620
  };
1448
1621
 
@@ -1452,10 +1625,9 @@ declare function createDisposer(): {
1452
1625
  * A debounced-search RxJS pipeline factory. Combines an input Subject with a
1453
1626
  * downstream fetch function and emits typed `{ results, isSearching }` state.
1454
1627
  *
1455
- * Designed to be created once per component instance via React's
1456
- * `useState(() => createSearchPipeline(...))` lazy initializer, then consumed
1457
- * via `useObservable(pipeline.state$)`. The pipeline holds no React state and
1458
- * has no React imports — it's pure RxJS, unit-testable without a renderer.
1628
+ * Designed to be created once per consumer instance and held for its lifetime
1629
+ * (e.g. by a view layer's lazy initializer), then observed via `state$`. The
1630
+ * pipeline is pure RxJS unit-testable without any view-layer dependency.
1459
1631
  *
1460
1632
  * The fetch function is expected to return `Observable<T[] | undefined>`,
1461
1633
  * matching the cache-miss-then-data shape of `BrowseNamespace` Observables:
@@ -1468,13 +1640,13 @@ interface SearchState<T> {
1468
1640
  isSearching: boolean;
1469
1641
  }
1470
1642
  interface SearchPipeline<T> {
1471
- /** Latest query string. Bind to a controlled input via `useObservable`. */
1643
+ /** Latest query string. Bind to a controlled input. */
1472
1644
  query$: Observable<string>;
1473
1645
  /** Latest search state — results plus a loading flag. */
1474
1646
  state$: Observable<SearchState<T>>;
1475
1647
  /** Push a new query value. Triggers the debounced fetch. */
1476
1648
  setQuery(value: string): void;
1477
- /** Tear down the input Subject. Call from `useEffect` cleanup. */
1649
+ /** Tear down the input Subject. Call from the consumer's cleanup hook. */
1478
1650
  dispose(): void;
1479
1651
  }
1480
1652
  interface SearchPipelineOptions {
@@ -1485,13 +1657,34 @@ interface SearchPipelineOptions {
1485
1657
  }
1486
1658
  declare function createSearchPipeline<T>(fetch: (query: string) => Observable<T[] | undefined>, options?: SearchPipelineOptions): SearchPipeline<T>;
1487
1659
 
1488
- interface BeckonVM extends ViewModel {
1660
+ /**
1661
+ * WorkerBus — minimal channel-bus surface that worker-side adapters
1662
+ * (e.g. `JobClaimAdapter` in `@semiont/jobs`, `SmelterActorStateUnit` in
1663
+ * `@semiont/make-meaning`) need.
1664
+ *
1665
+ * Transport-neutral by design. HTTP `ActorStateUnit` (from `@semiont/api-client`)
1666
+ * satisfies it directly; an in-process worker can pass a small shim around
1667
+ * an `EventBus` with a `() => Promise<void>` `emit` that calls into the
1668
+ * actor system.
1669
+ *
1670
+ * `addChannels` is optional because in-process buses receive every emit
1671
+ * implicitly — only HTTP needs to widen its SSE subscription set to
1672
+ * include worker-only channels (`job:queued`, `yield:created`, etc.).
1673
+ */
1674
+
1675
+ interface WorkerBus {
1676
+ on$<T = Record<string, unknown>>(channel: string): Observable<T>;
1677
+ emit(channel: string, payload: Record<string, unknown>): Promise<void>;
1678
+ addChannels?(channels: readonly string[]): void;
1679
+ }
1680
+
1681
+ interface BeckonStateUnit extends StateUnit {
1489
1682
  hoveredAnnotationId$: Observable<AnnotationId | null>;
1490
1683
  hover(annotationId: AnnotationId | null): void;
1491
1684
  focus(annotationId: AnnotationId): void;
1492
1685
  sparkle(annotationId: AnnotationId): void;
1493
1686
  }
1494
- declare function createBeckonVM(client: SemiontClient): BeckonVM;
1687
+ declare function createBeckonStateUnit(client: SemiontClient): BeckonStateUnit;
1495
1688
  /** Default milliseconds the mouse must dwell before beckon:hover is emitted. */
1496
1689
  declare const HOVER_DELAY_MS = 150;
1497
1690
  type EmitHover = (annotationId: AnnotationId | null) => void;
@@ -1502,372 +1695,48 @@ interface HoverHandlers {
1502
1695
  }
1503
1696
  declare function createHoverHandlers(emit: EmitHover, delayMs: number): HoverHandlers;
1504
1697
 
1505
- /**
1506
- * ShellVM — app-shell state: which toolbar panel is open, tab-bar
1507
- * coordination helpers, scroll-to-annotation signals. Lives on
1508
- * `SemiontBrowser`'s app-scoped bus (not the per-session client bus)
1509
- * because panel toggles and shell chrome must work regardless of
1510
- * whether a KB session is active.
1511
- *
1512
- * Channels: `panel:toggle`, `panel:open`, `panel:close`.
1513
- */
1514
-
1515
- type ToolbarPanelType = 'history' | 'info' | 'annotations' | 'settings' | 'collaboration' | 'user' | 'jsonld' | 'knowledge-base';
1516
- declare const COMMON_PANELS: readonly ToolbarPanelType[];
1517
- declare const RESOURCE_PANELS: readonly ToolbarPanelType[];
1518
- interface ShellVM extends ViewModel {
1519
- activePanel$: Observable<ToolbarPanelType | null>;
1520
- scrollToAnnotationId$: Observable<string | null>;
1521
- panelInitialTab$: Observable<{
1522
- tab: string;
1523
- generation: number;
1524
- } | null>;
1525
- openPanel(panel: string): void;
1526
- closePanel(): void;
1527
- togglePanel(panel: string): void;
1528
- onScrollCompleted(): void;
1529
- }
1530
- interface ShellVMOptions {
1531
- initialPanel?: ToolbarPanelType | null;
1532
- onPanelChange?: (panel: ToolbarPanelType | null) => void;
1533
- }
1534
- declare function createShellVM(browser: SemiontBrowser, options?: ShellVMOptions): ShellVM;
1535
-
1536
- interface GatherVM extends ViewModel {
1698
+ interface GatherStateUnit extends StateUnit {
1537
1699
  context$: Observable<GatheredContext | null>;
1538
1700
  loading$: Observable<boolean>;
1539
1701
  error$: Observable<Error | null>;
1540
1702
  annotationId$: Observable<AnnotationId | null>;
1541
1703
  }
1542
- declare function createGatherVM(client: SemiontClient, resourceId: ResourceId): GatherVM;
1704
+ declare function createGatherStateUnit(client: SemiontClient, resourceId: ResourceId): GatherStateUnit;
1543
1705
 
1544
- interface MatchVM extends ViewModel {
1706
+ interface MatchStateUnit extends StateUnit {
1545
1707
  }
1546
- declare function createMatchVM(client: SemiontClient, _resourceId: ResourceId): MatchVM;
1708
+ declare function createMatchStateUnit(client: SemiontClient, _resourceId: ResourceId): MatchStateUnit;
1547
1709
 
1548
1710
  type JobProgress$1 = components['schemas']['JobProgress'];
1549
1711
  interface GenerateDocumentOptions {
1550
1712
  title: string;
1551
1713
  storageUri: string;
1552
1714
  prompt?: string;
1715
+ /** Body locale — language the generated resource is written in. Falls back to the state unit's UI locale when unset. */
1553
1716
  language?: string;
1717
+ /** Source-resource locale — language of the resource the annotation lives on. Forwarded to the prompt for context-snippet awareness. BCP-47. */
1718
+ sourceLanguage?: string;
1554
1719
  temperature?: number;
1555
1720
  maxTokens?: number;
1556
1721
  context: GatheredContext;
1557
1722
  }
1558
- interface YieldVM extends ViewModel {
1723
+ interface YieldStateUnit extends StateUnit {
1559
1724
  isGenerating$: Observable<boolean>;
1560
1725
  progress$: Observable<JobProgress$1 | null>;
1561
1726
  generate(referenceId: string, options: GenerateDocumentOptions): void;
1562
1727
  }
1563
- declare function createYieldVM(client: SemiontClient, resourceId: ResourceId, locale: string): YieldVM;
1728
+ declare function createYieldStateUnit(client: SemiontClient, resourceId: ResourceId, locale: string): YieldStateUnit;
1564
1729
 
1565
1730
  type JobProgress = components['schemas']['JobProgress'];
1566
1731
  interface PendingAnnotation {
1567
1732
  selector: Selector | Selector[];
1568
1733
  motivation: Motivation;
1569
1734
  }
1570
- interface MarkVM extends ViewModel {
1735
+ interface MarkStateUnit extends StateUnit {
1571
1736
  pendingAnnotation$: Observable<PendingAnnotation | null>;
1572
1737
  assistingMotivation$: Observable<Motivation | null>;
1573
1738
  progress$: Observable<JobProgress | null>;
1574
1739
  }
1575
- declare function createMarkVM(client: SemiontClient, resourceId: ResourceId): MarkVM;
1576
-
1577
- interface DiscoverVM extends ViewModel {
1578
- browse: ShellVM;
1579
- search: SearchPipeline<ResourceDescriptor>;
1580
- recentResources$: Observable<ResourceDescriptor[]>;
1581
- entityTypes$: Observable<string[]>;
1582
- isLoadingRecent$: Observable<boolean>;
1583
- }
1584
- declare function createDiscoverVM(client: SemiontClient, browse: ShellVM): DiscoverVM;
1585
-
1586
- interface EntityTagsVM extends ViewModel {
1587
- browse: ShellVM;
1588
- entityTypes$: Observable<string[]>;
1589
- isLoading$: Observable<boolean>;
1590
- newTag$: Observable<string>;
1591
- error$: Observable<string>;
1592
- isAdding$: Observable<boolean>;
1593
- setNewTag(value: string): void;
1594
- addTag(): Promise<void>;
1595
- }
1596
- declare function createEntityTagsVM(client: SemiontClient, browse: ShellVM): EntityTagsVM;
1597
-
1598
- interface ImportPreview {
1599
- format: string;
1600
- version: number;
1601
- sourceUrl: string;
1602
- stats: Record<string, number>;
1603
- }
1604
- interface ExchangeVM extends ViewModel {
1605
- browse: ShellVM;
1606
- selectedFile$: Observable<File | null>;
1607
- preview$: Observable<ImportPreview | null>;
1608
- importPhase$: Observable<string | null>;
1609
- importMessage$: Observable<string | undefined>;
1610
- importResult$: Observable<Record<string, unknown> | undefined>;
1611
- isExporting$: Observable<boolean>;
1612
- isImporting$: Observable<boolean>;
1613
- selectFile(file: File): void;
1614
- cancelImport(): void;
1615
- doExport(): Promise<{
1616
- blob: Blob;
1617
- filename: string;
1618
- }>;
1619
- doImport(): Promise<void>;
1620
- }
1621
- declare function createExchangeVM(browse: ShellVM, exportFn: (params?: {
1622
- includeArchived?: boolean;
1623
- }) => Promise<Response>, importFn: (file: File, options?: {
1624
- onProgress?: (event: {
1625
- phase: string;
1626
- message?: string;
1627
- result?: Record<string, unknown>;
1628
- }) => void;
1629
- }) => Promise<{
1630
- phase: string;
1631
- message?: string;
1632
- result?: Record<string, unknown>;
1633
- }>): ExchangeVM;
1634
-
1635
- interface AdminUsersVM extends ViewModel {
1636
- browse: ShellVM;
1637
- users$: Observable<unknown[]>;
1638
- stats$: Observable<unknown | null>;
1639
- usersLoading$: Observable<boolean>;
1640
- statsLoading$: Observable<boolean>;
1641
- updateUser(id: string, data: {
1642
- isAdmin?: boolean;
1643
- isActive?: boolean;
1644
- }): Promise<void>;
1645
- }
1646
- declare function createAdminUsersVM(client: SemiontClient, browse: ShellVM): AdminUsersVM;
1647
-
1648
- interface AdminSecurityVM extends ViewModel {
1649
- browse: ShellVM;
1650
- providers$: Observable<unknown[]>;
1651
- allowedDomains$: Observable<string[]>;
1652
- isLoading$: Observable<boolean>;
1653
- }
1654
- declare function createAdminSecurityVM(client: SemiontClient, browse: ShellVM): AdminSecurityVM;
1655
-
1656
- interface WelcomeVM extends ViewModel {
1657
- userData$: Observable<{
1658
- termsAcceptedAt?: string;
1659
- } | null>;
1660
- isProcessing$: Observable<boolean>;
1661
- acceptTerms(): Promise<void>;
1662
- }
1663
- declare function createWelcomeVM(client: SemiontClient): WelcomeVM;
1664
-
1665
- interface ResourceLoaderVM extends ViewModel {
1666
- resource$: Observable<ResourceDescriptor | undefined>;
1667
- isLoading$: Observable<boolean>;
1668
- invalidate(): void;
1669
- }
1670
- declare function createResourceLoaderVM(client: SemiontClient, resourceId: ResourceId): ResourceLoaderVM;
1671
-
1672
- interface SessionVM extends ViewModel {
1673
- isLoggingOut$: Observable<boolean>;
1674
- logout(): Promise<void>;
1675
- }
1676
- declare function createSessionVM(client: SemiontClient): SessionVM;
1677
-
1678
- interface SmelterEvent {
1679
- type: string;
1680
- resourceId?: string;
1681
- payload: Record<string, unknown>;
1682
- }
1683
- interface SmelterActorVMOptions {
1684
- baseUrl: string;
1685
- token: string;
1686
- reconnectMs?: number;
1687
- }
1688
- interface SmelterActorVM extends ViewModel {
1689
- events$: Observable<SmelterEvent>;
1690
- state$: Observable<ConnectionState>;
1691
- emit(channel: string, payload: Record<string, unknown>): Promise<void>;
1692
- start(): void;
1693
- stop(): void;
1694
- }
1695
- declare function createSmelterActorVM(options: SmelterActorVMOptions): SmelterActorVM;
1696
-
1697
- /**
1698
- * Job Claim Adapter — worker-side job lifecycle glue on top of a
1699
- * shared `ActorVM`.
1700
- *
1701
- * Replaces the old `WorkerVM`, which owned its own actor and
1702
- * duplicated the SSE connection that `SemiontClient` already held.
1703
- * Workers now construct a `SemiontSession` normally (one actor, one
1704
- * SSE connection) and use this adapter to attach job-claim behaviour
1705
- * on top of `session.client.actor`.
1706
- *
1707
- * The adapter is intentionally thin: it subscribes to `job:queued`
1708
- * on the actor, claims jobs via the existing request-response
1709
- * protocol (`job:claim` → `job:claimed` / `job:claim-failed`), and
1710
- * exposes observables for job orchestration. It does **not** own
1711
- * the actor, has no HTTP concerns, and has no modal state.
1712
- */
1713
-
1714
- interface JobAssignment {
1715
- jobId: string;
1716
- type: string;
1717
- resourceId: string;
1718
- }
1719
- interface ActiveJob {
1720
- jobId: string;
1721
- type: string;
1722
- resourceId: string;
1723
- userId: string;
1724
- params: Record<string, unknown>;
1725
- }
1726
- interface JobClaimAdapterOptions {
1727
- /** Shared actor (typically `session.client.actor`). */
1728
- actor: ActorVM;
1729
- /**
1730
- * Job types this worker can process. Jobs of other types that
1731
- * arrive on `job:queued` are ignored. Empty array = accept any.
1732
- */
1733
- jobTypes: string[];
1734
- }
1735
- interface JobClaimAdapter {
1736
- /** Currently-claimed job, or null when idle. */
1737
- readonly activeJob$: Observable<ActiveJob | null>;
1738
- /** True while a claim is in flight or a job is being processed. */
1739
- readonly isProcessing$: Observable<boolean>;
1740
- /** Monotonically-incrementing count of successfully-completed jobs. */
1741
- readonly jobsCompleted$: Observable<number>;
1742
- /** Stream of job failures (including claim-failed and processing errors). */
1743
- readonly errors$: Observable<{
1744
- jobId: string;
1745
- error: string;
1746
- }>;
1747
- /**
1748
- * Subscribe to `job:queued` events (adding the channel to the actor
1749
- * if not already subscribed) and begin claiming matching jobs.
1750
- * Idempotent — calling `start()` twice is a no-op.
1751
- */
1752
- start(): void;
1753
- /** Stop claiming new jobs. Does not cancel an in-flight job. */
1754
- stop(): void;
1755
- /** Signal successful completion of `activeJob$`. */
1756
- completeJob(): void;
1757
- /** Signal failure of `activeJob$`. Emits on `errors$`. */
1758
- failJob(jobId: string, error: string): void;
1759
- /** Release observables. Does not dispose the shared actor. */
1760
- dispose(): void;
1761
- }
1762
- /**
1763
- * Attach job-claim behaviour to a shared `ActorVM`.
1764
- */
1765
- declare function createJobClaimAdapter(options: JobClaimAdapterOptions): JobClaimAdapter;
1766
-
1767
- interface Job {
1768
- jobId: string;
1769
- type: string;
1770
- status: string;
1771
- resourceId: string;
1772
- /** DID of the user who initiated the job (audit). */
1773
- userId: string;
1774
- created: string;
1775
- startedAt?: string;
1776
- completedAt?: string;
1777
- error?: string;
1778
- progress?: Record<string, unknown>;
1779
- result?: Record<string, unknown>;
1780
- }
1781
- interface JobQueueVM extends ViewModel {
1782
- jobs$: Observable<Job[]>;
1783
- pendingByType$: Observable<Map<string, number>>;
1784
- runningJobs$: Observable<Job[]>;
1785
- jobCreated$: Observable<Job>;
1786
- jobCompleted$: Observable<Job>;
1787
- jobFailed$: Observable<Job>;
1788
- }
1789
- declare function createJobQueueVM(client: SemiontClient): JobQueueVM;
1790
-
1791
- interface AnnotationGroups {
1792
- highlights: Annotation[];
1793
- comments: Annotation[];
1794
- assessments: Annotation[];
1795
- references: Annotation[];
1796
- tags: Annotation[];
1797
- }
1798
- type StoredEventResponse = components['schemas']['StoredEventResponse'];
1799
- interface WizardState {
1800
- open: boolean;
1801
- annotationId: string | null;
1802
- resourceId: string | null;
1803
- defaultTitle: string;
1804
- entityTypes: string[];
1805
- }
1806
- interface ResourceViewerPageVM extends ViewModel {
1807
- beckon: BeckonVM;
1808
- browse: ShellVM;
1809
- mark: MarkVM;
1810
- gather: GatherVM;
1811
- yield: YieldVM;
1812
- annotations$: Observable<Annotation[]>;
1813
- annotationGroups$: Observable<AnnotationGroups>;
1814
- entityTypes$: Observable<string[]>;
1815
- events$: Observable<StoredEventResponse[]>;
1816
- referencedBy$: Observable<ReferencedByEntry[]>;
1817
- content$: Observable<string>;
1818
- contentLoading$: Observable<boolean>;
1819
- mediaToken$: Observable<string | null>;
1820
- wizard$: Observable<WizardState>;
1821
- closeWizard(): void;
1822
- }
1823
- declare function createResourceViewerPageVM(client: SemiontClient, resourceId: ResourceId, locale: string, browse: ShellVM, options?: {
1824
- mediaType?: string;
1825
- }): ResourceViewerPageVM;
1826
-
1827
- type ComposeMode = 'new' | 'clone' | 'reference';
1828
- interface ComposeParams {
1829
- mode?: string | undefined;
1830
- token?: string | undefined;
1831
- annotationUri?: string | undefined;
1832
- sourceDocumentId?: string | undefined;
1833
- name?: string | undefined;
1834
- entityTypes?: string | undefined;
1835
- storedContext?: string | undefined;
1836
- }
1837
- interface CloneData {
1838
- sourceResource: ResourceDescriptor;
1839
- sourceContent: string;
1840
- }
1841
- interface ReferenceData {
1842
- annotationUri: string;
1843
- sourceDocumentId: string;
1844
- name: string;
1845
- entityTypes: string[];
1846
- }
1847
- interface SaveResourceParams {
1848
- mode: ComposeMode;
1849
- name: string;
1850
- storageUri: string;
1851
- content?: string;
1852
- file?: File;
1853
- format?: string;
1854
- charset?: string;
1855
- entityTypes?: string[];
1856
- language: string;
1857
- archiveOriginal?: boolean;
1858
- annotationUri?: string;
1859
- sourceDocumentId?: string;
1860
- }
1861
- interface ComposePageVM extends ViewModel {
1862
- browse: ShellVM;
1863
- mode$: Observable<ComposeMode>;
1864
- loading$: Observable<boolean>;
1865
- cloneData$: Observable<CloneData | null>;
1866
- referenceData$: Observable<ReferenceData | null>;
1867
- gatheredContext$: Observable<GatheredContext | null>;
1868
- entityTypes$: Observable<string[]>;
1869
- save(params: SaveResourceParams): Promise<string>;
1870
- }
1871
- declare function createComposePageVM(client: SemiontClient, browse: ShellVM, params: ComposeParams, auth?: AccessToken): ComposePageVM;
1740
+ declare function createMarkStateUnit(client: SemiontClient, resourceId: ResourceId): MarkStateUnit;
1872
1741
 
1873
- export { type ActiveJob, AdminNamespace, type AdminSecurityVM, type AdminUsersVM, type AnnotationGroups, type AnnotationHistoryResponse, AuthNamespace, BeckonNamespace, type BeckonVM, BindNamespace, BrowseNamespace, BusRequestError, type BusRequestErrorCode, type BusRequestPrimitive, COMMON_PANELS, type Cache, CacheObservable, 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, SemiontSession, type SemiontSessionConfig, SemiontSessionError, type SemiontSessionErrorCode, type SessionStorage, type SessionVM, type ShellVM, type ShellVMOptions, type SmelterActorVM, type SmelterActorVMOptions, type SmelterEvent, type StoredSession, StreamObservable, 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 };
1742
+ export { AdminNamespace, type AnnotationHistoryResponse, AuthNamespace, BeckonNamespace, type BeckonStateUnit, BindNamespace, BrowseNamespace, BusRequestError, type BusRequestErrorCode, type BusRequestPrimitive, CacheObservable, type CreateAnnotationInput, type CreateFromTokenOptions, type CreateResourceInput, FrameNamespace, type GatherAnnotationProgress, GatherNamespace, type GatherStateUnit, type GenerateDocumentOptions, type GenerationOptions, type GetBrowserOptions, HOVER_DELAY_MS, type HoverHandlers, type HttpEndpoint, InMemorySessionStorage, JobNamespace, type KbEndpoint, type KbSessionStatus, type KnowledgeBase, type LocalEndpoint, type MarkAssistEvent, type MarkAssistOptions, type MarkAssistProgress, MarkNamespace, type MarkStateUnit, MatchNamespace, type MatchSearchProgress, type MatchStateUnit, type NewKnowledgeBase, type OpenResource, type PendingAnnotation, type ReferencedByEntry, type RequestContent, type ResponseContent, type SearchPipeline, type SearchPipelineOptions, type SearchState, SemiontBrowser, type SemiontBrowserConfig, SemiontClient, SemiontSession, type SemiontSessionConfig, SemiontSessionError, type SemiontSessionErrorCode, type SessionFactory, type SessionFactoryOptions, SessionSignals, type SessionStorage, type StateUnit, type StoredSession, StreamObservable, UploadObservable, type UploadProgress, type User, type UserInfo, type WorkerBus, type YieldGenerationEvent, YieldNamespace, type YieldStateUnit, busRequest, createBeckonStateUnit, createDisposer, createGatherStateUnit, createHoverHandlers, createHttpSessionFactory, createMarkStateUnit, createMatchStateUnit, createSearchPipeline, createYieldStateUnit, defaultProtocol, getBrowser, httpKb, isValidHostname, kbBackendUrl, setStoredSession };