@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.
- package/LICENSE +201 -0
- package/README.md +109 -492
- package/dist/abstain.d.ts +43 -0
- package/dist/abstain.d.ts.map +1 -0
- package/dist/abstain.js +42 -0
- package/dist/abstain.js.map +1 -0
- package/dist/adapters/anthropic.d.ts +57 -0
- package/dist/adapters/anthropic.d.ts.map +1 -0
- package/dist/adapters/anthropic.js +57 -0
- package/dist/adapters/anthropic.js.map +1 -0
- package/dist/adapters/index.d.ts +16 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +16 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/langchain.d.ts +62 -0
- package/dist/adapters/langchain.d.ts.map +1 -0
- package/dist/adapters/langchain.js +68 -0
- package/dist/adapters/langchain.js.map +1 -0
- package/dist/adapters/memory.d.ts +105 -0
- package/dist/adapters/memory.d.ts.map +1 -0
- package/dist/adapters/memory.js +105 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/openai.d.ts +56 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +64 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/adapters/remaining.d.ts +52 -0
- package/dist/adapters/remaining.d.ts.map +1 -0
- package/dist/adapters/remaining.js +67 -0
- package/dist/adapters/remaining.js.map +1 -0
- package/dist/client.d.ts +512 -173
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +648 -504
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +28 -0
- package/dist/errors.js.map +1 -0
- package/dist/hash.d.ts +13 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +92 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +18 -109
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -876
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +809 -486
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -17
- package/dist/types.js.map +1 -1
- package/package.json +30 -103
- package/dist/client.d.mts +0 -224
- package/dist/client.mjs +0 -505
- package/dist/client.mjs.map +0 -1
- package/dist/index.d.mts +0 -111
- package/dist/index.mjs +0 -863
- package/dist/index.mjs.map +0 -1
- package/dist/providers/langchain.js +0 -821
- package/dist/providers/langchain.js.map +0 -1
- package/dist/providers/langchain.mjs +0 -816
- package/dist/providers/langchain.mjs.map +0 -1
- package/dist/providers/openai.js +0 -737
- package/dist/providers/openai.js.map +0 -1
- package/dist/providers/openai.mjs +0 -732
- package/dist/providers/openai.mjs.map +0 -1
- package/dist/providers/vercel.js +0 -866
- package/dist/providers/vercel.js.map +0 -1
- package/dist/providers/vercel.mjs +0 -860
- package/dist/providers/vercel.mjs.map +0 -1
- package/dist/react/hooks.d.mts +0 -327
- package/dist/react/hooks.d.ts +0 -327
- package/dist/react/hooks.js +0 -1183
- package/dist/react/hooks.js.map +0 -1
- package/dist/react/hooks.mjs +0 -1172
- package/dist/react/hooks.mjs.map +0 -1
- package/dist/types.d.mts +0 -494
- package/dist/types.mjs +0 -14
- package/dist/types.mjs.map +0 -1
- package/dist/websocket.d.mts +0 -160
- package/dist/websocket.d.ts +0 -160
- package/dist/websocket.js +0 -342
- package/dist/websocket.js.map +0 -1
- package/dist/websocket.mjs +0 -339
- 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
|
-
|
|
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
|
-
##
|
|
9
|
+
## Install
|
|
6
10
|
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
17
|
+
Node 18+ (uses the global `fetch`). Zero runtime dependencies.
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
import { Cilow } from '@cilow/sdk';
|
|
19
|
+
## Point it at a server
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
180
|
-
import { createContextMiddleware } from '@cilow/sdk/providers/vercel';
|
|
30
|
+
## Five lines
|
|
181
31
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
apiKey: 'your-key'
|
|
185
|
-
});
|
|
32
|
+
```ts
|
|
33
|
+
import { CilowClient, isGrounded } from "@cilow/sdk";
|
|
186
34
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
##
|
|
42
|
+
## The four core verbs
|
|
195
43
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
51
|
+
Plus `health()` (liveness probe, no auth).
|
|
201
52
|
|
|
202
|
-
|
|
203
|
-
apiUrl: 'https://api.cilow.ai',
|
|
204
|
-
apiKey: 'cilow-key',
|
|
205
|
-
autoStore: true // Automatically store conversations
|
|
206
|
-
});
|
|
53
|
+
### Options
|
|
207
54
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
217
|
-
|
|
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
|
-
|
|
220
|
-
|
|
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
|
-
|
|
70
|
+
### Full profile / legacy aliases
|
|
225
71
|
|
|
226
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
253
|
-
import {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import { CilowVectorStore } from '@cilow/sdk/providers/langchain';
|
|
101
|
+
## The deferral contract
|
|
276
102
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
//
|
|
292
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
450
|
-
ws.onGraphNodeCreated((event) => {
|
|
451
|
-
console.log('New node:', event.node);
|
|
452
|
-
});
|
|
131
|
+
## Errors
|
|
453
132
|
|
|
454
|
-
|
|
455
|
-
|
|
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.
|
|
505
|
-
} catch (
|
|
506
|
-
if (
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|