@semiont/sdk 0.5.0 → 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/README.md CHANGED
@@ -6,17 +6,81 @@
6
6
  [![npm downloads](https://img.shields.io/npm/dm/@semiont/sdk.svg)](https://www.npmjs.com/package/@semiont/sdk)
7
7
  [![License](https://img.shields.io/npm/l/@semiont/sdk.svg)](https://github.com/The-AI-Alliance/semiont/blob/main/LICENSE)
8
8
 
9
- The developer-facing SDK for [Semiont](https://github.com/The-AI-Alliance/semiont). This package owns the high-level surface every Semiont consumer reaches for: a verb-oriented `SemiontClient`, per-KB sessions, RxJS view-models, and the helpers that wire them all together.
9
+ The TypeScript SDK for [Semiont](https://github.com/The-AI-Alliance/semiont) a programmable surface for **collaborative knowledge work**. Whether you're building a browser app where humans annotate documents and propose links, an AI agent that gathers context and matches candidate references, a daemon that ingests new sources, or a one-shot script that queries an established knowledge base, you reach the same verb namespaces, the same collaboration primitives, the same lifecycle observables.
10
10
 
11
- The SDK is **transport-agnostic**it consumes the `ITransport` and `IContentTransport` contracts from [`@semiont/core`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/core). For HTTP, the canonical wire adapter is re-exported here for convenience. For in-process operation, use `LocalTransport` from [`@semiont/make-meaning`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/make-meaning).
11
+ The eight flows *frame, yield, mark, match, bind, gather, browse, beckon* describe what participants *do* when they work with a shared corpus. The first seven act on content; Frame acts on the schema layer (the conceptual vocabulary the others operate within). The SDK exposes them uniformly across surfaces. A human in a browser hovers an annotation; an AI agent at the other end of the bus sees the hover and reacts; a daemon ingests new text and every connected participant sees the corpus grow live. Humans and AI agents are peers — the SDK does not distinguish.
12
+
13
+ The SDK is **transport-agnostic**: it consumes the `ITransport` and `IContentTransport` contracts from [`@semiont/core`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/core). For HTTP backends, the canonical wire adapter is re-exported here for convenience. For in-process operation (CLI, agentic worker, embedded use), use `LocalTransport` from [`@semiont/make-meaning`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/make-meaning).
14
+
15
+ > **Where this doc fits.** This README is the *typed-surface reference* — what's in `@semiont/sdk`, how the namespaces are organized, what return shapes to expect. For the *protocol-level architectural framing* (the eight flows, the three programmable surfaces — CLI, SDK, Skills — the core tenets, the per-flow contracts), start with [`docs/protocol/README.md`](https://github.com/The-AI-Alliance/semiont/blob/main/docs/protocol/README.md). Daemon authors stitching multiple packages together also want the [skill packs](https://github.com/The-AI-Alliance/semiont/tree/main/docs/protocol/skills) — `semiont-session` for watcher daemons, `semiont-worker` for job-claim daemons, `semiont-wiki` for the end-to-end annotation pipeline.
16
+
17
+ ## Three ideas that hold the surface together
18
+
19
+ The SDK is wider than a typical client library because the domain is — collaborative knowledge work over a shared corpus, with humans and AI agents as peers. Three framings make the API tractable; once you've seen them, the rest is predictable.
20
+
21
+ ### 1. Eight verbs
22
+
23
+ Every operation in the SDK belongs to one of eight *flows* — verbs that describe what a participant *does* with a shared corpus. The flows are the entire vocabulary of the protocol; learn them once and the surface stays small.
24
+
25
+ | Verb | What it does | Example methods |
26
+ |---|---|---|
27
+ | **frame** | Define and evolve the schema vocabulary (entity types, future tag schemas, relation types) | `frame.addEntityType`, `frame.addEntityTypes` |
28
+ | **yield** | Introduce new resources into the system | `yield.resource`, `yield.fromAnnotation`, `yield.cloneToken` |
29
+ | **mark** | Add structured metadata to resources | `mark.annotation`, `mark.assist`, `mark.archive` |
30
+ | **match** | Search the corpus for candidate resources | `match.search` |
31
+ | **bind** | Resolve ambiguous references to specific resources | `bind.body`, `bind.initiate` |
32
+ | **gather** | Assemble related context around an annotation | `gather.annotation` |
33
+ | **browse** | Navigate, read, and observe | `browse.resource`, `browse.annotations`, `browse.click` |
34
+ | **beckon** | Coordinate attention across participants | `beckon.hover`, `beckon.attention`, `beckon.sparkle` |
35
+
36
+ Each flow is a namespace on `SemiontClient` (`client.mark.X(...)`, `client.gather.X(...)`, ...). The verb is the unit of mental model — a method call belongs to a flow, not to a noun. Frame is the schema-layer flow — content flows operate within the vocabulary Frame manages. Per-flow contracts live in [`docs/protocol/flows`](https://github.com/The-AI-Alliance/semiont/tree/main/docs/protocol/flows).
37
+
38
+ ### 2. Four return shapes — guess from the name
39
+
40
+ Every method on every namespace returns one of exactly four shapes, and the method name tells you which. Internalize the convention once and you stop having to read return types:
41
+
42
+ | Shape | Naming convention | Examples |
43
+ |---|---|---|
44
+ | **`Promise<T>`** — atomic backend op | past-tense or short noun | `mark.annotation`, `bind.body`, `auth.password` |
45
+ | **`StreamObservable<T>`** — long-running stream | plain verb | `mark.assist`, `match.search`, `gather.annotation` |
46
+ | **`CacheObservable<T>`** — live query | plain noun | `browse.resource`, `browse.annotations`, `browse.entityTypes` |
47
+ | **`void`** — collaboration signal | imperative or progressive verb | `beckon.hover`, `bind.initiate`, `mark.changeShape` |
48
+
49
+ Both Observable subclasses implement `PromiseLike<T>`, so consumers can `await` them directly without learning RxJS. Reach for `.subscribe(...)` when you want progress events, live updates, or to observe a collaboration signal another participant emitted. See [`docs/REACTIVE-MODEL.md`](https://github.com/The-AI-Alliance/semiont/blob/main/packages/sdk/docs/REACTIVE-MODEL.md) for the full design and method-by-method assignment.
50
+
51
+ ### 3. Collaboration primitives
52
+
53
+ The fourth row above — the `void`-returning collaboration signals — is the SDK's distinctive contribution to multi-participant coordination. They look fire-and-forget at the call site; on the bus they fan out across every participant.
54
+
55
+ A human in a browser hovers an annotation (`beckon.hover(annotationId)`); an AI agent at the other end of the bus sees `beckon:hover` and reacts. An agent emits a sparkle (`beckon.sparkle(annotationId)`); the human's UI lights up the indicated annotation. A frontend state unit emits `mark.changeShape('rectangle')`; a different participant subscribed to `mark:shape-changed` reacts.
56
+
57
+ This is *protocol-level* coordination — not browser-app fluff, not bolted-on presence — and it sits on the same typed namespace surface as data operations. Observers reach the same signals via `session.subscribe(channel, handler)` or `client.bus.get(channel)`. Three legitimate paths to the bus are documented in [`docs/REACTIVE-MODEL.md`](https://github.com/The-AI-Alliance/semiont/blob/main/packages/sdk/docs/REACTIVE-MODEL.md#three-paths-to-the-bus).
12
58
 
13
59
  ## What's in the box
14
60
 
15
61
  - **`SemiontClient`** — the verb-oriented coordinator over a wire transport.
16
62
  - **Verb namespaces** — `browse`, `mark`, `bind`, `gather`, `match`, `yield`, `beckon`, `job`, `auth`, `admin`. Typed methods that wrap the bus protocol; consumers never touch raw channel strings.
17
- - **Session layer** — `SemiontSession` (per-KB authentication, token refresh, lifecycle), `SemiontBrowser` (tab-singleton coordinator), and `SessionStorage` adapters (`InMemorySessionStorage`, plus a web one in `@semiont/react-ui`).
18
- - **View-models** — RxJS-based MVVM factories the React layer mounts via `useViewModel`.
19
- - **Helpers** — `bus-request` (correlation-ID request/reply) and `cache` (per-key SWR cache).
63
+ - **Collaboration primitives** — fire-and-forget signals on the verb namespaces (`beckon.hover`, `bind.initiate`, `mark.changeShape`, `browse.click`, ...) coordinate attention and intent across participants. Not afterthoughts, not browser-app fluff: they're how a multi-participant session stays coherent.
64
+ - **Session layer** — `SemiontSession` (per-KB authentication, token refresh, lifecycle), `SemiontBrowser` (multi-KB orchestration), and `SessionStorage` adapters (`InMemorySessionStorage`, plus a browser-backed one in `@semiont/react-ui`).
65
+ - **Flow state machines** — RxJS-based factories (`createMarkStateUnit`, `createGatherStateUnit`, `createMatchStateUnit`, `createYieldStateUnit`, `createBeckonStateUnit`) that wrap each long-running flow with `loading$` / `error$` / progress observables. UI-shape-agnostic any consumer (browser, terminal, mobile, daemon) can subscribe.
66
+ - **`WorkerBus`** — the transport-neutral channel-bus interface that worker-side adapters consume. Domain-specific worker adapters live with their domain — `createJobClaimAdapter` and `createJobQueueStateUnit` in [`@semiont/jobs`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/jobs); `createSmelterActorStateUnit` in [`@semiont/make-meaning`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/make-meaning) — and consume `WorkerBus` from here.
67
+ - **Helpers** — `bus-request` (correlation-ID request/reply), the cache primitive backing live queries, and `createSearchPipeline` (debounced-search RxJS pipeline).
68
+
69
+ Page-shaped state machines (admin tables, compose page, resource viewer page, etc.) live in [`@semiont/react-ui`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/react-ui), alongside the components that render them. Those are framework-neutral but tied to the Semiont web frontend's specific page taxonomy; they don't apply to non-web consumers.
70
+
71
+ ## For non-web consumers (TUI, mobile, daemon, agent)
72
+
73
+ `@semiont/sdk` is the only package a non-web Semiont consumer needs. From it you get:
74
+
75
+ - `SemiontClient` + the eight verb namespaces (`frame`, `browse`, `mark`, `bind`, `gather`, `match`, `yield`, `beckon`)
76
+ - Three infrastructure namespaces (`auth`, `admin`, `job`) when constructed with backend operations
77
+ - `SemiontSession` for long-running token refresh + persistence
78
+ - `SemiontBrowser` for multi-KB orchestration (transport-agnostic; takes a `SessionFactory`)
79
+ - The five flow state machines above
80
+ - The transport-neutral `WorkerBus` interface (worker adapters live in their domain packages — `@semiont/jobs`, `@semiont/make-meaning`)
81
+ - Branded ID types, the unified error hierarchy, the `TransportErrorCode` neutral vocabulary
82
+
83
+ Nothing page-shaped, nothing web-shell-shaped. A TUI, mobile reader, daemon, or AI agent installs `@semiont/sdk` alone (plus a transport package — `@semiont/api-client` for HTTP, `@semiont/make-meaning` for in-process).
20
84
 
21
85
  ## Installation
22
86
 
@@ -26,42 +90,54 @@ npm install @semiont/sdk
26
90
 
27
91
  ## Quick start (HTTP)
28
92
 
93
+ For one-shot scripts, `SemiontClient.signInHttp(...)` is the credentials-first one-line construction:
94
+
29
95
  ```ts
30
- import {
31
- SemiontSession,
32
- InMemorySessionStorage,
33
- setStoredSession,
34
- type KnowledgeBase,
35
- } from '@semiont/sdk';
36
- import { accessToken } from '@semiont/core';
37
- import { firstValueFrom } from 'rxjs';
38
- import { filter } from 'rxjs/operators';
96
+ import { SemiontClient } from '@semiont/sdk';
97
+
98
+ const semiont = await SemiontClient.signInHttp({
99
+ baseUrl: 'http://localhost:4000',
100
+ email: 'me@example.com',
101
+ password: 'pwd',
102
+ });
103
+
104
+ const resources = await semiont.browse.resources({ limit: 10 });
105
+ console.log(resources);
106
+
107
+ semiont.dispose();
108
+ ```
109
+
110
+ For long-running scripts that need to survive token expiry, use `SemiontSession.signInHttp(...)` — same credentials shape, plus proactive refresh, validation, storage-adapter wiring, and disposal. `kb` is required; its `id` is the storage key for this session, so distinct scripts must use distinct ids:
111
+
112
+ ```ts
113
+ import { SemiontSession, InMemorySessionStorage, type KnowledgeBase } from '@semiont/sdk';
39
114
 
40
115
  const kb: KnowledgeBase = {
41
- id: 'local',
42
- label: 'Local Backend',
43
- protocol: 'http',
44
- host: 'localhost',
45
- port: 4000,
116
+ id: 'my-watcher',
117
+ label: 'My Watcher',
46
118
  email: 'me@example.com',
119
+ endpoint: { kind: 'http', host: 'localhost', port: 4000, protocol: 'http' },
47
120
  };
48
121
 
49
- const storage = new InMemorySessionStorage();
50
- setStoredSession(storage, kb.id, {
51
- access: accessToken('your-jwt'),
52
- refresh: '',
122
+ const session = await SemiontSession.signInHttp({
123
+ kb,
124
+ storage: new InMemorySessionStorage(),
125
+ baseUrl: 'http://localhost:4000',
126
+ email: 'me@example.com',
127
+ password: 'pwd',
53
128
  });
54
129
 
55
- const session = await SemiontSession.create({ kb, storage });
130
+ // session.client is the same SemiontClient surface; the session manages
131
+ // the token$ lifecycle around it (default refresh callback wired automatically).
132
+ const resources = await session.client.browse.resources({ limit: 10 });
56
133
 
57
- const resources = await firstValueFrom(
58
- session.client.browse.resources({ limit: 10 }).pipe(
59
- filter((r): r is NonNullable<typeof r> => r !== undefined),
60
- ),
61
- );
62
- console.log(resources);
134
+ await session.dispose();
63
135
  ```
64
136
 
137
+ `KnowledgeBase` is uniform regardless of transport kind; the variation lives in the nested `endpoint` (currently `{ kind: 'http', host, port, protocol }` or `{ kind: 'local', kbId }`). Code that doesn't construct transports — your scripts, the verb namespaces, the flow state machines — never inspects `endpoint`.
138
+
139
+ If you already have an access token (CLI cached-token path, env-var token, embedded auth flow), use `SemiontClient.fromHttp({ baseUrl, token })` or `SemiontSession.fromHttp({ baseUrl, token, storage, kb, refresh, ... })` to skip the auth round-trip.
140
+
65
141
  ## Quick start (in-process)
66
142
 
67
143
  When you want the SDK without an HTTP backend — e.g. in a CLI, a unit test, or an Electron-style desktop app — wire `LocalTransport` directly to a knowledge system:
@@ -86,39 +162,55 @@ const client = new SemiontClient(
86
162
  );
87
163
  ```
88
164
 
89
- Same `SemiontClient`, same verb namespaces — no network involved.
165
+ Same `SemiontClient`, same verb namespaces — no network involved. There is no `fromLocal` factory because the in-process transport's dependencies (knowledgeSystem, eventBus, userId) are not boilerplate the SDK can hide.
90
166
 
91
- ## Verb namespaces
167
+ ## Worked examples
92
168
 
93
- All ten namespaces hang off `SemiontClient`. Each method either returns a `Promise` (one-shot RPC-style operations) or an `Observable` (streaming subscriptions). The bus is invisible to callers channel strings, correlation IDs, and reconnection are internal.
169
+ The eight verb namespaces hang off `SemiontClient`, plus three infrastructure namespaces (`auth`, `admin`, `job`) when the client was constructed with backend operations. Each example below uses one of the four return shapes from the table above; pick whichever matches what your call site needs.
94
170
 
95
171
  ```ts
96
- // Browse — read the knowledge graph.
97
- await client.browse.resources({ limit: 10 });
172
+ // Browse — live queries; await yields the loaded value, subscribe yields
173
+ // loading-then-loaded.
174
+ const resources = await client.browse.resources({ limit: 10 });
98
175
  client.browse.resource(resourceId).subscribe(/* ... */);
99
176
 
100
- // Mark / Bind — create and modify annotations.
101
- const { annotationId } = await client.mark.annotation(rid, request);
177
+ // Mark / Bind — atomic operations return Promise<T>.
178
+ // `mark.annotation` takes the W3C-shaped input directly; the resourceId
179
+ // is derived from `input.target.source` and returned as a branded id.
180
+ const { annotationId } = await client.mark.annotation(annotationInput);
102
181
  await client.bind.body(rid, aid, [{ op: 'add', item: { /* W3C body */ } }]);
103
182
 
104
- // Gather / Match — assemble context and run semantic search.
105
- const ctx = await lastValueFrom(client.gather.annotation(aid, rid));
183
+ // Gather / Match — bounded streams; await yields the final value, subscribe
184
+ // yields every progress emission.
185
+ const ctx = await client.gather.annotation(rid, aid);
106
186
  client.match.search(rid, refId, ctx, { limit: 10 }).subscribe(/* ... */);
107
187
 
108
- // Yield — author new resources.
188
+ // Yield — author new resources. Returns an UploadObservable; await yields
189
+ // { resourceId }, subscribe yields the upload-progress lifecycle.
109
190
  const { resourceId } = await client.yield.resource({
110
191
  name, file, format, storageUri,
111
192
  });
112
193
 
113
- // Beckon — UI signals (hover, focus, selection).
194
+ // Beckon, Bind, Browse, Mark collaboration signals (void). Fire-and-
195
+ // forget; fan out to other participants over the bus.
114
196
  client.beckon.hover(annotationId);
197
+ client.bind.initiate({ annotationId });
198
+ client.browse.click(annotationId, 'linking');
115
199
  ```
116
200
 
117
- The verb-by-verb walkthroughs live in [docs/flows](https://github.com/The-AI-Alliance/semiont/tree/main/docs/flows).
201
+ The verb-by-verb walkthroughs live in [docs/protocol/flows](https://github.com/The-AI-Alliance/semiont/tree/main/docs/protocol/flows). The per-namespace API reference with concrete examples for each method lives in [`docs/Usage.md`](https://github.com/The-AI-Alliance/semiont/blob/main/packages/sdk/docs/Usage.md).
202
+
203
+ ## Documentation
204
+
205
+ - [`docs/Usage.md`](https://github.com/The-AI-Alliance/semiont/blob/main/packages/sdk/docs/Usage.md) — per-namespace tour with concrete examples for Browse, Mark, Bind, Gather, Match, Yield, Beckon, Auth, Admin, Job, plus SSE and error handling.
206
+ - [`docs/REACTIVE-MODEL.md`](https://github.com/The-AI-Alliance/semiont/blob/main/packages/sdk/docs/REACTIVE-MODEL.md) — the Promise-shape-over-Observable design: how `await` works on the SDK's return values without learning RxJS, and where RxJS is still visible by design.
207
+ - [`docs/STATE-UNITS.md`](https://github.com/The-AI-Alliance/semiont/blob/main/packages/sdk/docs/STATE-UNITS.md) — the foundational pattern behind the flow state machines, worker adapters, and search pipeline: closure-based factories, RxJS-shaped surface, dispose lifecycle, and the axioms every new state unit honors.
208
+ - [`docs/CACHE-SEMANTICS.md`](https://github.com/The-AI-Alliance/semiont/blob/main/packages/sdk/docs/CACHE-SEMANTICS.md) — the cache primitive's behavioral contract.
209
+ - [`docs/protocol/TRANSPORT-CONTRACT.md`](https://github.com/The-AI-Alliance/semiont/blob/main/docs/protocol/TRANSPORT-CONTRACT.md) — the transport interface every `ITransport` must honor.
118
210
 
119
211
  ## Behavioral contract
120
212
 
121
- The guarantees every `ITransport` implementation must honor — what `subscribe()` does on disconnect, what `LastEventId` replay must look like, what `puts` must be idempotent — are documented in [packages/core/docs/TRANSPORT-CONTRACT.md](https://github.com/The-AI-Alliance/semiont/blob/main/packages/core/docs/TRANSPORT-CONTRACT.md). HTTP-specific guarantees (the `/bus/emit` gateway, SSE reconnect, `Last-Event-ID` replay window) live alongside the backend at [apps/backend/docs/TRANSPORT.md](https://github.com/The-AI-Alliance/semiont/blob/main/apps/backend/docs/TRANSPORT.md).
213
+ The guarantees every `ITransport` implementation must honor — what `subscribe()` does on disconnect, what `LastEventId` replay must look like, what `puts` must be idempotent — are documented in [docs/protocol/TRANSPORT-CONTRACT.md](https://github.com/The-AI-Alliance/semiont/blob/main/docs/protocol/TRANSPORT-CONTRACT.md). HTTP-specific guarantees (the `/bus/emit` gateway, SSE reconnect, `Last-Event-ID` replay window) live in [docs/protocol/TRANSPORT-HTTP.md](https://github.com/The-AI-Alliance/semiont/blob/main/docs/protocol/TRANSPORT-HTTP.md).
122
214
 
123
215
  When implementing a new transport (gRPC, WebSocket, IPC, …), implement those interfaces from `@semiont/core` directly — there is no inheritance from `HttpTransport`.
124
216
 
@@ -132,4 +224,4 @@ Apache-2.0 — see [LICENSE](https://github.com/The-AI-Alliance/semiont/blob/mai
132
224
  - [`@semiont/api-client`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/api-client) — HTTP transport (`HttpTransport`, `HttpContentTransport`)
133
225
  - [`@semiont/make-meaning`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/make-meaning) — in-process transport (`LocalTransport`) and the actor model behind it
134
226
  - [`@semiont/observability`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/observability) — OpenTelemetry tracing the SDK propagates across the bus
135
- - [`@semiont/react-ui`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/react-ui) — React bindings (`useViewModel`, web `SessionStorage`)
227
+ - [`@semiont/react-ui`](https://github.com/The-AI-Alliance/semiont/tree/main/packages/react-ui) — React bindings (`useStateUnit`, web `SessionStorage`)