@cilow/sdk 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +109 -492
  3. package/dist/abstain.d.ts +43 -0
  4. package/dist/abstain.d.ts.map +1 -0
  5. package/dist/abstain.js +42 -0
  6. package/dist/abstain.js.map +1 -0
  7. package/dist/adapters/anthropic.d.ts +57 -0
  8. package/dist/adapters/anthropic.d.ts.map +1 -0
  9. package/dist/adapters/anthropic.js +57 -0
  10. package/dist/adapters/anthropic.js.map +1 -0
  11. package/dist/adapters/index.d.ts +16 -0
  12. package/dist/adapters/index.d.ts.map +1 -0
  13. package/dist/adapters/index.js +16 -0
  14. package/dist/adapters/index.js.map +1 -0
  15. package/dist/adapters/langchain.d.ts +62 -0
  16. package/dist/adapters/langchain.d.ts.map +1 -0
  17. package/dist/adapters/langchain.js +68 -0
  18. package/dist/adapters/langchain.js.map +1 -0
  19. package/dist/adapters/memory.d.ts +105 -0
  20. package/dist/adapters/memory.d.ts.map +1 -0
  21. package/dist/adapters/memory.js +105 -0
  22. package/dist/adapters/memory.js.map +1 -0
  23. package/dist/adapters/openai.d.ts +56 -0
  24. package/dist/adapters/openai.d.ts.map +1 -0
  25. package/dist/adapters/openai.js +64 -0
  26. package/dist/adapters/openai.js.map +1 -0
  27. package/dist/adapters/remaining.d.ts +52 -0
  28. package/dist/adapters/remaining.d.ts.map +1 -0
  29. package/dist/adapters/remaining.js +67 -0
  30. package/dist/adapters/remaining.js.map +1 -0
  31. package/dist/client.d.ts +512 -173
  32. package/dist/client.d.ts.map +1 -0
  33. package/dist/client.js +648 -504
  34. package/dist/client.js.map +1 -1
  35. package/dist/errors.d.ts +25 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +28 -0
  38. package/dist/errors.js.map +1 -0
  39. package/dist/hash.d.ts +13 -0
  40. package/dist/hash.d.ts.map +1 -0
  41. package/dist/hash.js +92 -0
  42. package/dist/hash.js.map +1 -0
  43. package/dist/index.d.ts +18 -109
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +16 -876
  46. package/dist/index.js.map +1 -1
  47. package/dist/types.d.ts +809 -486
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +18 -17
  50. package/dist/types.js.map +1 -1
  51. package/package.json +30 -103
  52. package/dist/client.d.mts +0 -224
  53. package/dist/client.mjs +0 -505
  54. package/dist/client.mjs.map +0 -1
  55. package/dist/index.d.mts +0 -111
  56. package/dist/index.mjs +0 -863
  57. package/dist/index.mjs.map +0 -1
  58. package/dist/providers/langchain.js +0 -821
  59. package/dist/providers/langchain.js.map +0 -1
  60. package/dist/providers/langchain.mjs +0 -816
  61. package/dist/providers/langchain.mjs.map +0 -1
  62. package/dist/providers/openai.js +0 -737
  63. package/dist/providers/openai.js.map +0 -1
  64. package/dist/providers/openai.mjs +0 -732
  65. package/dist/providers/openai.mjs.map +0 -1
  66. package/dist/providers/vercel.js +0 -866
  67. package/dist/providers/vercel.js.map +0 -1
  68. package/dist/providers/vercel.mjs +0 -860
  69. package/dist/providers/vercel.mjs.map +0 -1
  70. package/dist/react/hooks.d.mts +0 -327
  71. package/dist/react/hooks.d.ts +0 -327
  72. package/dist/react/hooks.js +0 -1183
  73. package/dist/react/hooks.js.map +0 -1
  74. package/dist/react/hooks.mjs +0 -1172
  75. package/dist/react/hooks.mjs.map +0 -1
  76. package/dist/types.d.mts +0 -494
  77. package/dist/types.mjs +0 -14
  78. package/dist/types.mjs.map +0 -1
  79. package/dist/websocket.d.mts +0 -160
  80. package/dist/websocket.d.ts +0 -160
  81. package/dist/websocket.js +0 -342
  82. package/dist/websocket.js.map +0 -1
  83. package/dist/websocket.mjs +0 -339
  84. package/dist/websocket.mjs.map +0 -1
package/README.md CHANGED
@@ -1,535 +1,152 @@
1
- # @cilow/sdk
1
+ # @cilow/sdk — TypeScript
2
2
 
3
- Complete TypeScript SDK for Cilow - AI Memory Infrastructure with multi-framework support.
3
+ The TypeScript client for **Cilow**, the verifiable context layer for AI agents. Point it at
4
+ a running [`cilow-serve`](../../docs/user_guide/mcp_server.md) and call **four core verbs** —
5
+ `ingest`, `contextPack`, `forget`, `explain` — the same four the MCP core profile advertises.
6
+ The headline feature is **calibrated deferral**: every read tells you when the engine
7
+ *doesn't know* instead of fabricating an answer.
4
8
 
5
- ## Installation
9
+ ## Install
6
10
 
7
- ```bash
8
- npm install @cilow/sdk
9
- # or
10
- yarn add @cilow/sdk
11
- # or
12
- pnpm add @cilow/sdk
11
+ ```sh
12
+ # From this repo (no registry publish yet):
13
+ cd sdk/typescript && npm install && npm run build
14
+ # In your app, depend on the built package or vendor `dist/`.
13
15
  ```
14
16
 
15
- ## Quick Start
17
+ Node 18+ (uses the global `fetch`). Zero runtime dependencies.
16
18
 
17
- ```typescript
18
- import { Cilow } from '@cilow/sdk';
19
+ ## Point it at a server
19
20
 
20
- const cilow = new Cilow({
21
- apiUrl: 'https://api.cilow.ai',
22
- apiKey: 'your-api-key'
23
- });
24
-
25
- // Store a memory
26
- await cilow.remember("User prefers dark mode", { tags: ["preference", "ui"] });
27
-
28
- // Search memories
29
- const memories = await cilow.recall("user preferences");
30
-
31
- // Delete memories
32
- await cilow.forget({ tags: ["temporary"] });
33
- ```
34
-
35
- ## Features
36
-
37
- - **Simple API**: `remember`, `recall`, `forget` for easy memory management
38
- - **Full TypeScript Support**: Complete type definitions for all APIs
39
- - **Framework Integrations**: Vercel AI SDK, OpenAI, LangChain
40
- - **React Hooks**: `useMemory`, `useRecall`, `useMemoryContext`
41
- - **WebSocket Support**: Real-time memory updates and graph changes
42
- - **Multi-tier Memory**: Hot, warm, cold memory tiers for optimal performance
43
-
44
- ## Core Client
45
-
46
- ### Basic Operations
47
-
48
- ```typescript
49
- import { Cilow } from '@cilow/sdk';
50
-
51
- const cilow = new Cilow({
52
- apiUrl: 'https://api.cilow.ai',
53
- apiKey: 'your-api-key'
54
- });
55
-
56
- // Store a memory
57
- const memoryId = await cilow.remember("Important information", {
58
- tags: ["important"],
59
- userId: "user-123",
60
- metadata: { source: "chat" }
61
- });
62
-
63
- // Search memories with semantic similarity
64
- const results = await cilow.recall("important information", {
65
- limit: 10,
66
- minRelevance: 0.5,
67
- tags: ["important"]
68
- });
69
-
70
- // Delete memories
71
- await cilow.forget({ memoryId: "mem-123" });
72
- await cilow.forget({ tags: ["old"], userId: "user-123" });
73
- ```
74
-
75
- ### Advanced Operations
76
-
77
- ```typescript
78
- // Get a specific memory
79
- const memory = await cilow.getMemory("mem-123");
80
-
81
- // Update a memory
82
- const updated = await cilow.updateMemory("mem-123", {
83
- content: "Updated content",
84
- tags: ["updated"]
85
- });
86
-
87
- // List memories with pagination
88
- const list = await cilow.listMemories({
89
- limit: 20,
90
- offset: 0,
91
- userId: "user-123",
92
- tags: ["preference"]
93
- });
94
-
95
- // Get context for AI prompts
96
- const context = await cilow.getContext("recent discussions", {
97
- maxTokens: 2000,
98
- userId: "user-123"
99
- });
100
-
101
- // Store conversation turns
102
- await cilow.storeConversation({
103
- userMessage: "What's the weather?",
104
- assistantResponse: "The weather is sunny today.",
105
- userId: "user-123",
106
- sessionId: "session-abc"
107
- });
108
-
109
- // Get statistics
110
- const stats = await cilow.getStats();
111
- console.log(`Total memories: ${stats.totalMemories}`);
112
- ```
113
-
114
- ### Graph Operations
115
-
116
- ```typescript
117
- // Create graph nodes
118
- const node = await cilow.createGraphNode("Person", "John Doe", {
119
- email: "john@example.com"
120
- });
121
-
122
- // Create relationships
123
- const edge = await cilow.createGraphEdge(
124
- node.id,
125
- "other-node-id",
126
- "KNOWS",
127
- { since: "2024" }
128
- );
129
-
130
- // Traverse the graph
131
- const subgraph = await cilow.traverseGraph({
132
- startNodeId: node.id,
133
- maxDepth: 3,
134
- relationshipTypes: ["KNOWS", "WORKS_WITH"]
135
- });
21
+ ```sh
22
+ cargo build --profile fast --features serve --bin cilow-serve
23
+ CILOW_DB=/tmp/cilow.log CILOW_TOKENS="demo:1:1" ./target/fast/cilow-serve
24
+ # listening on http://0.0.0.0:8080 · bearer token "demo"
136
25
  ```
137
26
 
138
- ## Vercel AI SDK Integration
139
-
140
- ```typescript
141
- import { createCilowTools, createCilowContext } from '@cilow/sdk/providers/vercel';
142
- import { streamText } from 'ai';
143
- import { anthropic } from '@ai-sdk/anthropic';
144
-
145
- // Create tools for AI to use
146
- const tools = createCilowTools({
147
- apiUrl: 'https://api.cilow.ai',
148
- apiKey: 'your-key',
149
- defaultUserId: 'user-123'
150
- });
151
-
152
- // Use with streamText
153
- const result = await streamText({
154
- model: anthropic('claude-sonnet-4-20250514'),
155
- tools,
156
- messages: [
157
- { role: 'user', content: 'What do you remember about me?' }
158
- ]
159
- });
160
-
161
- // Or use context injection
162
- const getContext = createCilowContext({
163
- apiUrl: 'https://api.cilow.ai',
164
- apiKey: 'your-key',
165
- maxTokens: 2000
166
- });
167
-
168
- const context = await getContext('previous discussions');
169
-
170
- const response = await streamText({
171
- model: anthropic('claude-sonnet-4-20250514'),
172
- system: `You are a helpful assistant.\n\nContext:\n${context.context}`,
173
- prompt: 'Continue our discussion'
174
- });
175
- ```
176
-
177
- ### Context Middleware
27
+ The token maps to a tenant scope that the server **stamps** onto every request — a client can
28
+ never read another tenant's memory, and a client-supplied `scope` is ignored over HTTP.
178
29
 
179
- ```typescript
180
- import { createContextMiddleware } from '@cilow/sdk/providers/vercel';
30
+ ## Five lines
181
31
 
182
- const withMemory = createContextMiddleware({
183
- apiUrl: 'https://api.cilow.ai',
184
- apiKey: 'your-key'
185
- });
32
+ ```ts
33
+ import { CilowClient, isGrounded } from "@cilow/sdk";
186
34
 
187
- // Automatically inject memory context
188
- const messagesWithContext = await withMemory(originalMessages, {
189
- userId: 'user-123',
190
- maxTokens: 2000
191
- });
35
+ const cilow = new CilowClient({ baseUrl: "http://localhost:8080", token: "demo" });
36
+ await cilow.ingest({ facts: [{ subject: "Maya", predicate: "employer", object: "Acme" }] });
37
+ const r = await cilow.contextPack("where does Maya work", { anchor: "Maya", attribute: "employer" });
38
+ console.log(r.abstained ? `don't know: ${r.reason}` : r.pack); // the injectible pack; citations on r.citations
39
+ console.log(isGrounded(await cilow.explain(r.trace_id!))); // why it surfaced, vs the tenant threshold
192
40
  ```
193
41
 
194
- ## OpenAI SDK Integration
42
+ ## The four core verbs
195
43
 
196
- ```typescript
197
- import OpenAI from 'openai';
198
- import { createCilowOpenAI } from '@cilow/sdk/providers/openai';
44
+ | Verb | Signature | Returns | What it does |
45
+ |------|-----------|---------|--------------|
46
+ | `ingest` | `ingest({ facts } \| { text } \| { url } \| { contentBase64, mime? }, opts?)` | `IngestResponse` | The **one write door**. Never rejects: known kinds extract, unknown kinds are retained and recallable. A new value for an exclusive attribute *supersedes* the old one (history stays queryable). |
47
+ | `contextPack` | `contextPack(query: string, opts?)` | `ContextPackResponse` | The **product read**: a ready-to-inject `pack`, structured `citations` / `claims` with `is_current`, and the faithfulness fields `abstained` / `reason_code` / `grounding` / `pack_sufficient` / `as_of` / `trace_id`. No free-form synthesis. |
48
+ | `forget` | `forget(entity: string, opts?)` | `ForgetResponse` | **Withdraw**, not erase history: recall stops returning it; as-of-T reads before `at` still see it. `{ erase: true }` (or `erase()`) is the destructive right-to-be-forgotten path. |
49
+ | `explain` | `explain(traceId: number, opts?)` | `ExplainResponse` | **Why** a pack/recall surfaced what it surfaced: the cited claims (labels resolved, inclusion probability each), `best_nonconformity` vs the tenant threshold `q_hat`, and `isGrounded(r)`. `found: false` + `error` is in-band. |
199
50
 
200
- const openai = new OpenAI();
51
+ Plus `health()` (liveness probe, no auth).
201
52
 
202
- const cilowAI = createCilowOpenAI(openai, {
203
- apiUrl: 'https://api.cilow.ai',
204
- apiKey: 'cilow-key',
205
- autoStore: true // Automatically store conversations
206
- });
53
+ ### Options
207
54
 
208
- // Memory context is automatically injected
209
- const { completion, memoriesUsed } = await cilowAI.chat.completions.create({
210
- model: 'gpt-4',
211
- messages: [
212
- { role: 'user', content: 'Based on our previous discussions, what should I focus on?' }
213
- ]
214
- });
55
+ - `ingest`: `{ scope?, sourceId?, observedAt?, source?, rawContext?, branch?, subjectHint?, metadata? }` —
56
+ `source` / `rawContext` / `branch` apply to the facts form; `subjectHint` / `metadata` to text / url / bytes.
57
+ - `contextPack`: `{ scope?, now?, asOf?, anchor?, attribute? }`
58
+ - `forget`: `{ scope?, attribute?, value?, erase?, at? }`
59
+ - `explain`: `{ scope? }`
215
60
 
216
- console.log(`Used ${memoriesUsed} memories for context`);
217
- console.log(completion.choices[0].message.content);
61
+ `ingest` takes **exactly one** content form. `sourceId` (the idempotency key the wire requires)
62
+ defaults to a **content hash** — `ingestSourceId(content)` — so re-sending the same payload is a
63
+ no-op; the key actually sent is echoed on `source_id`. `kind` tells you which server path ran
64
+ (`"facts"` → the `remember` receipt on `remember`; `"content"` → the raw-durable receipt on
65
+ `content` with `blob_id` / `detected` / `reader_error`).
218
66
 
219
- // Direct memory access
220
- await cilowAI.remember("Important insight", { tags: ["insight"] });
221
- const memories = await cilowAI.recall("insights");
222
- ```
67
+ `anchor` (an entity surface form like `"Maya"`) + `attribute` (a predicate like `"employer"`)
68
+ take the exact-key **fast path**. Omit them for free-text semantic reads — same response shape.
223
69
 
224
- ## LangChain Integration
70
+ ### Full profile / legacy aliases
225
71
 
226
- ### Memory Class
72
+ The engine's full tool profile (`CILOW_MCP_PROFILE=full`) also advertises the older verbs, and
73
+ the client keeps them as aliases with their original receipts:
227
74
 
228
- ```typescript
229
- import { CilowMemory } from '@cilow/sdk/providers/langchain';
230
- import { ConversationChain } from 'langchain/chains';
231
- import { ChatOpenAI } from '@langchain/openai';
75
+ | Alias | Signature | Returns |
76
+ |-------|-----------|---------|
77
+ | `remember` | `remember(facts: Fact[], { scope?, source?, sourceId?, observedAt?, rawContext?, branch? })` | `RememberResponse` (write receipt) |
78
+ | `recall` | `recall(query: string, { scope?, asOf?, now?, anchor?, attribute?, branch? })` | `RecallResponse` (`abstained`, `claims`, `receipt`) |
79
+ | `answer` | `answer(query: string, { scope?, now?, anchor?, attribute?, branch? })` | `AnswerResponse` (`abstained`, `text`, `citations`) |
80
+ | `timeline` | `timeline(entity: string, { scope?, at?, from?, to? })` | `TimelineEntry[]` (bitemporal history) |
81
+ | `trackEntity` | `trackEntity(name: string, { scope?, at? })` | `TrackEntityResponse` (canonical id + alias class) |
232
82
 
233
- const memory = new CilowMemory({
234
- apiUrl: 'https://api.cilow.ai',
235
- apiKey: 'cilow-key',
236
- userId: 'user-123',
237
- sessionId: 'session-abc'
238
- });
83
+ Also available: `rememberText`, `ingestImage` / `ingestAudio` / `ingestPdf` / `ingestStructured`,
84
+ `save` / `ingestAny`, `feedback`, `consolidate`, `fork` / `branchList`, `blobGet` / `blobMeta` /
85
+ `blobList`, and the `cilow.memories.*` id-based REST surface (also exposed to frameworks via
86
+ `@cilow/sdk/adapters`).
239
87
 
240
- const chain = new ConversationChain({
241
- llm: new ChatOpenAI(),
242
- memory
243
- });
244
-
245
- const response = await chain.call({
246
- input: 'What did we discuss last time?'
247
- });
248
- ```
88
+ ### Time is in microseconds
249
89
 
250
- ### Retriever for RAG
90
+ Every time field (`observedAt`, `asOf`, `now`, `at`, `from`, `to`, and the `valid_from` /
91
+ `valid_until` on timeline entries) is **epoch microseconds (µs)**, matching the engine. They all
92
+ default to "now" (`Date.now() * 1000`). Use the exported `nowMicros()` helper if you need it:
251
93
 
252
- ```typescript
253
- import { CilowRetriever } from '@cilow/sdk/providers/langchain';
254
- import { RetrievalQAChain } from 'langchain/chains';
255
-
256
- const retriever = new CilowRetriever({
257
- apiUrl: 'https://api.cilow.ai',
258
- apiKey: 'cilow-key',
259
- topK: 5
260
- });
261
-
262
- const chain = RetrievalQAChain.fromLLM(
263
- new ChatOpenAI(),
264
- retriever
265
- );
266
-
267
- const answer = await chain.call({
268
- query: 'What are the key points from our meetings?'
269
- });
94
+ ```ts
95
+ import { nowMicros } from "@cilow/sdk";
96
+ const t = nowMicros();
97
+ await cilow.remember(facts, { observedAt: t });
98
+ const past = await cilow.recall("...", { asOf: t - 86_400_000_000, now: t }); // a day ago
270
99
  ```
271
100
 
272
- ### Vector Store
273
-
274
- ```typescript
275
- import { CilowVectorStore } from '@cilow/sdk/providers/langchain';
101
+ ## The deferral contract
276
102
 
277
- const vectorStore = new CilowVectorStore({
278
- apiUrl: 'https://api.cilow.ai',
279
- apiKey: 'cilow-key'
280
- });
103
+ `contextPack` (and the `recall` / `answer` aliases) **never throw** for a deferral — they return
104
+ a normal response with a boolean flag. When `abstained` is `true`, the pack / claims / citations
105
+ are empty and `reason_code` says why: `below_threshold` / `no_candidates` are a *calibrated*
106
+ abstain (memory genuinely lacks grounded context — do not fabricate past it), while
107
+ `provider_unavailable` is an *infrastructure* abstain (retry when the provider recovers). Branch
108
+ with `isCalibratedAbstain(r)` / `isInfrastructureAbstain(r)` rather than matching strings.
281
109
 
282
- // Add documents
283
- await vectorStore.addDocuments([
284
- { pageContent: 'Meeting notes from Monday', metadata: { topic: 'standup' } },
285
- { pageContent: 'Product requirements document', metadata: { topic: 'product' } }
286
- ]);
110
+ On `answer` / `contextPack` an abstain also names **which layer stopped it** — `r.terminal_layer`
111
+ (`structural` | `gate` | `provider` | `reader` | `verifier`; `stoppedAt(r)` is the same thing gated on
112
+ `abstained`). Only `provider` is retryable. Both surfaces also carry `r.evidence`: the claim, span,
113
+ and verbatim lanes collapsed into one list ordered by fused cross-lane rank (`Evidence.origins`
114
+ says which lanes agreed; `Evidence.structure` is present when a typed claim backs the unit). It is
115
+ present on abstains too — the raw lanes have no standing at the gate, but you may still read them.
287
116
 
288
- // Similarity search
289
- const results = await vectorStore.similaritySearch('standup notes', 5);
290
-
291
- // With scores
292
- const resultsWithScores = await vectorStore.similaritySearchWithScore('standup notes');
293
- ```
294
-
295
- ## React Hooks
296
-
297
- ```tsx
298
- import {
299
- CilowProvider,
300
- useMemory,
301
- useRecall,
302
- useMemoryContext,
303
- useMemorySubscription,
304
- useDebouncedSearch
305
- } from '@cilow/sdk/react';
306
-
307
- // Wrap your app with the provider
308
- function App() {
309
- return (
310
- <CilowProvider
311
- apiUrl="https://api.cilow.ai"
312
- apiKey="your-key"
313
- userId="user-123"
314
- enableWebSocket={true}
315
- >
316
- <YourApp />
317
- </CilowProvider>
318
- );
319
- }
320
-
321
- // Use memory operations
322
- function MemoryManager() {
323
- const { remember, forget, isLoading, error } = useMemory();
324
-
325
- const handleSave = async () => {
326
- await remember("Important note", { tags: ["note"] });
327
- };
328
-
329
- return (
330
- <button onClick={handleSave} disabled={isLoading}>
331
- Save Memory
332
- </button>
333
- );
334
- }
335
-
336
- // Search memories
337
- function SearchComponent() {
338
- const { data, search, isLoading } = useRecall();
339
-
340
- const handleSearch = () => {
341
- search("user preferences", { limit: 10 });
342
- };
343
-
344
- return (
345
- <div>
346
- <button onClick={handleSearch}>Search</button>
347
- {data.map(result => (
348
- <div key={result.memory.id}>
349
- <p>{result.memory.content}</p>
350
- <span>Score: {(result.score * 100).toFixed(0)}%</span>
351
- </div>
352
- ))}
353
- </div>
354
- );
355
- }
356
-
357
- // Debounced search
358
- function LiveSearch() {
359
- const { query, setQuery, results, isLoading } = useDebouncedSearch({
360
- delay: 300,
361
- minLength: 2
362
- });
363
-
364
- return (
365
- <div>
366
- <input
367
- value={query}
368
- onChange={e => setQuery(e.target.value)}
369
- placeholder="Search memories..."
370
- />
371
- {isLoading && <p>Searching...</p>}
372
- {results.map(r => (
373
- <div key={r.memory.id}>{r.memory.content}</div>
374
- ))}
375
- </div>
376
- );
377
- }
378
-
379
- // Get context for AI
380
- function ChatWithContext() {
381
- const { getContext, isLoading } = useMemoryContext();
382
-
383
- const sendMessage = async (message: string) => {
384
- const { context, memoriesUsed } = await getContext(message);
385
-
386
- // Use context with your AI provider
387
- const response = await callAI({
388
- system: `Context:\n${context}`,
389
- message
390
- });
391
- };
392
-
393
- return <ChatUI onSend={sendMessage} />;
394
- }
395
-
396
- // Real-time updates
397
- function LiveMemoryFeed() {
398
- const { latestEvent, events, isConnected } = useMemorySubscription({
399
- userId: 'user-123',
400
- eventTypes: ['memory.created', 'memory.updated']
401
- });
402
-
403
- useEffect(() => {
404
- if (latestEvent?.type === 'memory.created') {
405
- console.log('New memory:', latestEvent.memory);
406
- }
407
- }, [latestEvent]);
408
-
409
- return (
410
- <div>
411
- <p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
412
- <p>Events: {events.length}</p>
413
- </div>
414
- );
117
+ ```ts
118
+ const r = await cilow.contextPack("what is Maya's salary", { anchor: "Maya", attribute: "salary" });
119
+ if (r.abstained) {
120
+ // r.pack is empty; r.reason explains the configured gate decision.
121
+ return `I don't have that in memory (${r.reason}).`;
415
122
  }
123
+ return r.pack;
416
124
  ```
417
125
 
418
- ## WebSocket Support
419
-
420
- ```typescript
421
- import { CilowWebSocket } from '@cilow/sdk/websocket';
422
-
423
- const ws = new CilowWebSocket({
424
- apiUrl: 'https://api.cilow.ai',
425
- apiKey: 'your-key',
426
- reconnectAttempts: 5,
427
- heartbeatInterval: 30000
428
- });
429
-
430
- // Connect
431
- await ws.connect();
432
-
433
- // Subscribe to events
434
- ws.subscribe({ userId: 'user-123' });
435
-
436
- // Listen for memory events
437
- ws.onMemoryCreated((event) => {
438
- console.log('New memory:', event.memory);
439
- });
440
-
441
- ws.onMemoryUpdated((event) => {
442
- console.log('Updated memory:', event.memory);
443
- });
444
-
445
- ws.onMemoryDeleted((event) => {
446
- console.log('Deleted memory ID:', event.memoryId);
447
- });
126
+ Every read carries a `trace_id`. Hand it to `explain` for the audit trail — the cited claims,
127
+ the tenant's conformal threshold `q_hat`, the best candidate's `best_nonconformity` (grounded iff
128
+ `n ≤ q̂`, see `isGrounded`) — or to `feedback` so memory learns from the outcome. This is the
129
+ difference between "I don't know" and a confident wrong answer.
448
130
 
449
- // Listen for graph events
450
- ws.onGraphNodeCreated((event) => {
451
- console.log('New node:', event.node);
452
- });
131
+ ## Errors
453
132
 
454
- // Connection status
455
- ws.onConnectionStatus((event) => {
456
- console.log('Connection:', event.status, event.reason);
457
- });
458
-
459
- // Wait for specific event
460
- const event = await ws.once('memory.created', 5000);
461
-
462
- // Disconnect
463
- ws.disconnect();
464
- ```
465
-
466
- ## TypeScript Types
467
-
468
- The SDK exports comprehensive TypeScript types:
469
-
470
- ```typescript
471
- import type {
472
- // Core types
473
- Memory,
474
- MemorySummary,
475
- MemoryTier,
476
- SearchResult,
477
-
478
- // Graph types
479
- GraphNode,
480
- GraphEdge,
481
- GraphSubgraph,
482
-
483
- // Event types
484
- CilowEvent,
485
- MemoryCreatedEvent,
486
- MemoryUpdatedEvent,
487
-
488
- // Configuration
489
- CilowConfig,
490
- WebSocketConfig,
491
-
492
- // API types
493
- PaginatedResponse,
494
- ContextResult,
495
- } from '@cilow/sdk';
496
- ```
497
-
498
- ## Error Handling
499
-
500
- ```typescript
501
- import { CilowApiError, isApiError } from '@cilow/sdk';
133
+ `CilowError` is thrown for genuine failures — auth (`401` / JSON-RPC `-32001`), bad params,
134
+ transport errors, malformed responses. A deferral is not an error.
502
135
 
136
+ ```ts
137
+ import { CilowError } from "@cilow/sdk";
503
138
  try {
504
- await cilow.getMemory('invalid-id');
505
- } catch (error) {
506
- if (error instanceof CilowApiError) {
507
- console.log('API Error:', error.message);
508
- console.log('Code:', error.code);
509
- console.log('Status:', error.statusCode);
510
- console.log('Details:', error.details);
511
- }
139
+ await cilow.recall("...");
140
+ } catch (e) {
141
+ if (e instanceof CilowError && e.isUnauthorized) { /* bad/expired token */ }
512
142
  }
513
143
  ```
514
144
 
515
- ## Configuration Options
516
-
517
- ```typescript
518
- const cilow = new Cilow({
519
- // Required
520
- apiUrl: 'https://api.cilow.ai',
521
- apiKey: 'your-api-key',
522
-
523
- // Optional
524
- timeout: 30000, // Request timeout (ms)
525
- retries: 3, // Retry attempts
526
- debug: false, // Enable debug logging
527
- headers: { // Custom headers
528
- 'X-Custom-Header': 'value'
529
- }
530
- });
531
- ```
532
-
533
- ## License
145
+ ## Develop
534
146
 
535
- MIT
147
+ ```sh
148
+ npm install
149
+ npm run build # tsc → dist/
150
+ npm test # node:test — client + adapters suites, mocks the HTTP layer (no server needed)
151
+ npm run example # runs examples/quickstart.ts against a live server
152
+ ```
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Ergonomic helpers over the abstention contract carried by `recall` / `answer`.
3
+ *
4
+ * On abstain the engine returns `abstained: true`, an empty evidence payload, and a machine
5
+ * `reason_code`. These guards encode the control-flow split so a caller branches on typed state,
6
+ * not raw strings. Calibration quality is an external, versioned measurement.
7
+ */
8
+ import type { ExplainReport, ExplainResponse, ReasonCode, TerminalLayer } from "./types.js";
9
+ /** The minimal shape both `RecallResponse` and `AnswerResponse` satisfy. */
10
+ export interface Abstainable {
11
+ abstained: boolean;
12
+ reason_code?: ReasonCode;
13
+ /** Present on `answer` / `context_pack`; `recall` does not carry it. */
14
+ terminal_layer?: TerminalLayer;
15
+ }
16
+ /**
17
+ * True iff this is a retryable INFRASTRUCTURE abstain (the embedding provider failed), not a
18
+ * calibrated "memory does not know". Memory MAY hold the answer — a retry can succeed once the
19
+ * provider recovers.
20
+ */
21
+ export declare function isInfrastructureAbstain(r: Abstainable): boolean;
22
+ /**
23
+ * Which layer stopped the answer (see `TerminalLayer`), or `undefined` when the read answered or
24
+ * the surface does not carry the field (`recall`).
25
+ */
26
+ export declare function stoppedAt(r: Abstainable): TerminalLayer | undefined;
27
+ /**
28
+ * True iff memory abstained because it genuinely lacks grounded context (`below_threshold` /
29
+ * `no_candidates`). Do NOT fabricate past it; ask the user or search elsewhere.
30
+ */
31
+ export declare function isCalibratedAbstain(r: Abstainable): boolean;
32
+ /**
33
+ * Alias for {@link isInfrastructureAbstain} — a retry may succeed once the provider recovers. A
34
+ * calibrated abstain should NOT be retried verbatim (memory has been asked and does not know).
35
+ */
36
+ export declare function shouldRetry(r: Abstainable): boolean;
37
+ /**
38
+ * True iff an `explain` report says the read cleared the gate: found, not abstained, and
39
+ * `best_nonconformity <= q_hat` (the engine's definition of "grounded"). Accepts the whole
40
+ * `ExplainResponse` (a not-found lookup is never grounded) or a bare `ExplainReport`.
41
+ */
42
+ export declare function isGrounded(r: ExplainResponse | ExplainReport): boolean;
43
+ //# sourceMappingURL=abstain.d.ts.map