korely-memory 0.1.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 +21 -0
- package/README.md +117 -0
- package/dist/index.cjs +389 -0
- package/dist/index.d.cts +305 -0
- package/dist/index.d.ts +305 -0
- package/dist/index.js +354 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Korely
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# korely-memory
|
|
2
|
+
|
|
3
|
+
The JavaScript / TypeScript SDK for [Korely Agents](https://korely.ai/agents) —
|
|
4
|
+
memory for AI agents, with a typed bi-temporal knowledge graph behind every
|
|
5
|
+
write. A thin, **zero-dependency** client over the Korely REST API (uses the
|
|
6
|
+
native `fetch`). Same package name as the Python twin on
|
|
7
|
+
[PyPI](https://pypi.org/project/korely-memory/).
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install korely-memory
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Korely } from "korely-memory";
|
|
17
|
+
|
|
18
|
+
const korely = new Korely({ apiKey: "kor_live_..." }); // or set KORELY_API_KEY
|
|
19
|
+
|
|
20
|
+
// Remember something your agent learned about an end user
|
|
21
|
+
await korely.add("Maria prefers email over Slack, and her renewal is in October.", {
|
|
22
|
+
user_id: "customer-4812",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Later — even in a new session — pull a prompt-ready block back
|
|
26
|
+
const ctx = await korely.getContext({
|
|
27
|
+
query: "how does Maria like to be contacted?",
|
|
28
|
+
user_id: "customer-4812",
|
|
29
|
+
});
|
|
30
|
+
console.log(ctx.context); // drop straight into your system prompt
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That's the whole loop: `add` to remember, `getContext` (or `search`) to recall.
|
|
34
|
+
Behind `add`, Korely extracts typed, bi-temporal facts and builds a graph — you
|
|
35
|
+
just hand it text.
|
|
36
|
+
|
|
37
|
+
## Methods
|
|
38
|
+
|
|
39
|
+
Every method maps to one REST endpoint.
|
|
40
|
+
|
|
41
|
+
| Method | Endpoint | |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `add(content, opts?)` | `POST /v1/memories` | Write. `content` is a string or a list of chat messages. |
|
|
44
|
+
| `search(query, opts?)` | `POST /v1/memories/search` | Hybrid search over memories. |
|
|
45
|
+
| `getAll(opts?)` | `GET /v1/memories` | List a scope, newest first. |
|
|
46
|
+
| `get(id)` | `GET /v1/memories/:id` | One memory, with its facts. |
|
|
47
|
+
| `update(id, { content })` | `PATCH /v1/memories/:id` | Re-runs extraction. |
|
|
48
|
+
| `delete(id)` | `DELETE /v1/memories/:id` | Forget one (audited). |
|
|
49
|
+
| `deleteAll({ user_id })` | `DELETE /v1/users/:user_id/memories` | Forget everything for a user (GDPR). |
|
|
50
|
+
| `history(id)` | `GET /v1/memories/:id/history` | A memory's timeline + the facts it produced. |
|
|
51
|
+
| `users(opts?)` | `GET /v1/users` | Your end users, with counts. |
|
|
52
|
+
| `getFacts(opts?)` | `GET /v1/facts` | Typed facts; `as_of` for point-in-time. |
|
|
53
|
+
| `addFactTriple(s, p, o, opts?)` | `POST /v1/facts` | Write a fact directly (bi-temporal). |
|
|
54
|
+
| `getProfile({ user_id, as_of? })` | `GET /v1/profile` | The assembled profile of one end user. |
|
|
55
|
+
| `getContext({ query, user_id? })` | `GET /v1/context` | One call → a prompt-ready context block. |
|
|
56
|
+
| `batch(memories)` | `POST /v1/batch` | Bulk import, for migrations. |
|
|
57
|
+
| `batchStatus(jobId)` | `GET /v1/batch/:id` | Poll an import job. |
|
|
58
|
+
|
|
59
|
+
## Bi-temporal facts (the moat)
|
|
60
|
+
|
|
61
|
+
Every write extracts typed `(subject, predicate, object)` facts with validity in
|
|
62
|
+
time. Ask what was true on a past date:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// What did we know about this user on March 1st?
|
|
66
|
+
const past = await korely.getProfile({ user_id: "customer-4812", as_of: "2026-03-01" });
|
|
67
|
+
|
|
68
|
+
// Write a fact directly, dated in the past
|
|
69
|
+
await korely.addFactTriple("Marco", "works_at", "Acme GmbH", {
|
|
70
|
+
user_id: "customer-4812",
|
|
71
|
+
valid_from: "2026-06-01",
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Errors
|
|
76
|
+
|
|
77
|
+
Every error subclasses `KorelyError`, so one `catch` covers them all:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { Korely, QuotaExceededError, AuthenticationError } from "korely-memory";
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
await korely.add("...", { user_id: "u" });
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (e instanceof QuotaExceededError) {
|
|
86
|
+
console.log(`Rate limited — retry after ${e.retryAfter}s`);
|
|
87
|
+
} else if (e instanceof AuthenticationError) {
|
|
88
|
+
console.log("Bad or missing API key");
|
|
89
|
+
} else {
|
|
90
|
+
throw e;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`AuthenticationError` (401) · `NamespaceForbiddenError` (403) · `NotFoundError`
|
|
96
|
+
(404) · `StaleWriteError` (409) · `QuotaExceededError` (429, carries
|
|
97
|
+
`retryAfter`) · `APIError` (everything else).
|
|
98
|
+
|
|
99
|
+
## Configuration
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
new Korely({
|
|
103
|
+
apiKey: "kor_live_...", // or the KORELY_API_KEY env var
|
|
104
|
+
region: "eu", // EU only — data stored and processed in the EU
|
|
105
|
+
timeoutMs: 30000,
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Requires Node 18+ (native `fetch`), or any runtime with a global `fetch`. On
|
|
110
|
+
older runtimes, pass one via `{ fetch }`.
|
|
111
|
+
|
|
112
|
+
## Docs
|
|
113
|
+
|
|
114
|
+
- [SDK reference](https://korely.ai/agents/docs/surfaces/sdk)
|
|
115
|
+
- [REST API reference](https://korely.ai/agents/docs/api-reference)
|
|
116
|
+
|
|
117
|
+
MIT © Korely
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
APIError: () => APIError,
|
|
24
|
+
AuthenticationError: () => AuthenticationError,
|
|
25
|
+
Korely: () => Korely,
|
|
26
|
+
KorelyError: () => KorelyError,
|
|
27
|
+
NamespaceForbiddenError: () => NamespaceForbiddenError,
|
|
28
|
+
NotFoundError: () => NotFoundError,
|
|
29
|
+
QuotaExceededError: () => QuotaExceededError,
|
|
30
|
+
StaleWriteError: () => StaleWriteError,
|
|
31
|
+
VERSION: () => VERSION
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(index_exports);
|
|
34
|
+
|
|
35
|
+
// src/errors.ts
|
|
36
|
+
var KorelyError = class extends Error {
|
|
37
|
+
constructor(message = "", opts = {}) {
|
|
38
|
+
super(message || opts.code || "Korely error");
|
|
39
|
+
this.name = "KorelyError";
|
|
40
|
+
this.status = opts.status;
|
|
41
|
+
this.code = opts.code;
|
|
42
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var AuthenticationError = class _AuthenticationError extends KorelyError {
|
|
46
|
+
constructor(message = "", opts = {}) {
|
|
47
|
+
super(message, opts);
|
|
48
|
+
this.name = "AuthenticationError";
|
|
49
|
+
Object.setPrototypeOf(this, _AuthenticationError.prototype);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var NamespaceForbiddenError = class _NamespaceForbiddenError extends KorelyError {
|
|
53
|
+
constructor(message = "", opts = {}) {
|
|
54
|
+
super(message, opts);
|
|
55
|
+
this.name = "NamespaceForbiddenError";
|
|
56
|
+
Object.setPrototypeOf(this, _NamespaceForbiddenError.prototype);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var NotFoundError = class _NotFoundError extends KorelyError {
|
|
60
|
+
constructor(message = "", opts = {}) {
|
|
61
|
+
super(message, opts);
|
|
62
|
+
this.name = "NotFoundError";
|
|
63
|
+
Object.setPrototypeOf(this, _NotFoundError.prototype);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var StaleWriteError = class _StaleWriteError extends KorelyError {
|
|
67
|
+
constructor(message = "", opts = {}) {
|
|
68
|
+
super(message, opts);
|
|
69
|
+
this.name = "StaleWriteError";
|
|
70
|
+
Object.setPrototypeOf(this, _StaleWriteError.prototype);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var QuotaExceededError = class _QuotaExceededError extends KorelyError {
|
|
74
|
+
constructor(message = "", opts = {}) {
|
|
75
|
+
super(message, opts);
|
|
76
|
+
this.name = "QuotaExceededError";
|
|
77
|
+
this.retryAfter = opts.retryAfter;
|
|
78
|
+
Object.setPrototypeOf(this, _QuotaExceededError.prototype);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var APIError = class _APIError extends KorelyError {
|
|
82
|
+
constructor(message = "", opts = {}) {
|
|
83
|
+
super(message, opts);
|
|
84
|
+
this.name = "APIError";
|
|
85
|
+
Object.setPrototypeOf(this, _APIError.prototype);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// src/client.ts
|
|
90
|
+
var VERSION = "0.1.0";
|
|
91
|
+
var REGIONS = { eu: "https://api.korely.ai" };
|
|
92
|
+
function coerceContent(content) {
|
|
93
|
+
if (typeof content === "string") return content;
|
|
94
|
+
if (Array.isArray(content)) {
|
|
95
|
+
const parts = [];
|
|
96
|
+
for (const m of content) {
|
|
97
|
+
if (m && typeof m === "object") {
|
|
98
|
+
const role = String(m.role ?? "").trim();
|
|
99
|
+
const raw = m.content;
|
|
100
|
+
const body = raw == null ? "" : String(raw).trim();
|
|
101
|
+
if (role && body) parts.push(`${role}: ${body}`);
|
|
102
|
+
else if (body) parts.push(body);
|
|
103
|
+
} else {
|
|
104
|
+
const s = String(m).trim();
|
|
105
|
+
if (s) parts.push(s);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return parts.join("\n");
|
|
109
|
+
}
|
|
110
|
+
return String(content);
|
|
111
|
+
}
|
|
112
|
+
var Korely = class {
|
|
113
|
+
constructor(opts = {}) {
|
|
114
|
+
const envKey = typeof process !== "undefined" ? process.env?.KORELY_API_KEY : void 0;
|
|
115
|
+
const key = opts.apiKey ?? envKey;
|
|
116
|
+
if (!key) {
|
|
117
|
+
throw new KorelyError(
|
|
118
|
+
"No API key. Pass { apiKey: 'kor_live_...' } or set KORELY_API_KEY."
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
this.apiKey = key;
|
|
122
|
+
this.baseUrl = (opts.baseUrl ?? REGIONS[opts.region ?? "eu"] ?? REGIONS.eu).replace(/\/+$/, "");
|
|
123
|
+
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
124
|
+
const f = opts.fetch ?? globalThis.fetch;
|
|
125
|
+
if (!f) {
|
|
126
|
+
throw new KorelyError(
|
|
127
|
+
"No fetch available. On Node < 18 pass a fetch implementation via { fetch }."
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
this.fetchImpl = f;
|
|
131
|
+
}
|
|
132
|
+
// ── transport ─────────────────────────────────────────────────────────────
|
|
133
|
+
async request(method, path, opts = {}) {
|
|
134
|
+
let url = this.baseUrl + path;
|
|
135
|
+
if (opts.params) {
|
|
136
|
+
const qs = new URLSearchParams();
|
|
137
|
+
for (const [k, v] of Object.entries(opts.params)) {
|
|
138
|
+
if (v !== void 0 && v !== null) qs.append(k, String(v));
|
|
139
|
+
}
|
|
140
|
+
const s = qs.toString();
|
|
141
|
+
if (s) url += "?" + s;
|
|
142
|
+
}
|
|
143
|
+
const headers = {
|
|
144
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
145
|
+
Accept: "application/json",
|
|
146
|
+
"X-Korely-Client": `korely-js/${VERSION}`
|
|
147
|
+
};
|
|
148
|
+
let body;
|
|
149
|
+
if (opts.body !== void 0) {
|
|
150
|
+
body = JSON.stringify(opts.body);
|
|
151
|
+
headers["Content-Type"] = "application/json";
|
|
152
|
+
}
|
|
153
|
+
const controller = new AbortController();
|
|
154
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
155
|
+
let resp;
|
|
156
|
+
try {
|
|
157
|
+
resp = await this.fetchImpl(url, {
|
|
158
|
+
method,
|
|
159
|
+
headers,
|
|
160
|
+
body,
|
|
161
|
+
signal: controller.signal
|
|
162
|
+
});
|
|
163
|
+
} catch (e) {
|
|
164
|
+
throw new KorelyError(`Connection error: ${e?.message ?? String(e)}`);
|
|
165
|
+
} finally {
|
|
166
|
+
clearTimeout(timer);
|
|
167
|
+
}
|
|
168
|
+
const text = await resp.text();
|
|
169
|
+
let parsed = {};
|
|
170
|
+
if (text) {
|
|
171
|
+
try {
|
|
172
|
+
parsed = JSON.parse(text);
|
|
173
|
+
} catch {
|
|
174
|
+
parsed = { message: text };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (!resp.ok) {
|
|
178
|
+
this.raise(resp.status, parsed, resp.headers.get("retry-after"));
|
|
179
|
+
}
|
|
180
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
181
|
+
}
|
|
182
|
+
raise(status, body, retryAfter) {
|
|
183
|
+
const code = body?.code;
|
|
184
|
+
const msg = body?.message || code || `HTTP ${status}`;
|
|
185
|
+
if (status === 401) throw new AuthenticationError(msg, { status, code });
|
|
186
|
+
if (status === 403) throw new NamespaceForbiddenError(msg, { status, code });
|
|
187
|
+
if (status === 404) throw new NotFoundError(msg, { status, code });
|
|
188
|
+
if (status === 409) throw new StaleWriteError(msg, { status, code });
|
|
189
|
+
if (status === 429) {
|
|
190
|
+
const raw = retryAfter ?? body?.retry_after ?? body?._retry_after;
|
|
191
|
+
let ra;
|
|
192
|
+
if (raw != null) {
|
|
193
|
+
const n = parseInt(String(raw), 10);
|
|
194
|
+
ra = Number.isNaN(n) ? void 0 : n;
|
|
195
|
+
}
|
|
196
|
+
throw new QuotaExceededError(msg, { status, code, retryAfter: ra });
|
|
197
|
+
}
|
|
198
|
+
throw new APIError(msg, { status, code });
|
|
199
|
+
}
|
|
200
|
+
// ── memories ────────────────────────────────────────────────────────────
|
|
201
|
+
/**
|
|
202
|
+
* POST /v1/memories — store a memory; resolves to it with extracted facts.
|
|
203
|
+
* `content` is a string, or a list of chat messages (role/content), joined
|
|
204
|
+
* into one block before sending.
|
|
205
|
+
*/
|
|
206
|
+
async add(content, opts = {}) {
|
|
207
|
+
const text = coerceContent(content);
|
|
208
|
+
if (!text.trim()) {
|
|
209
|
+
throw new KorelyError(
|
|
210
|
+
"content is empty \u2014 pass a non-blank string or messages with content."
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return this.request("POST", "/v1/memories", {
|
|
214
|
+
body: {
|
|
215
|
+
content: text,
|
|
216
|
+
agent_id: opts.agent_id,
|
|
217
|
+
user_id: opts.user_id,
|
|
218
|
+
run_id: opts.run_id,
|
|
219
|
+
metadata: opts.metadata
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/** POST /v1/memories/search — hybrid retrieval, ranked by score. */
|
|
224
|
+
async search(query, opts = {}) {
|
|
225
|
+
const body = await this.request("POST", "/v1/memories/search", {
|
|
226
|
+
body: {
|
|
227
|
+
query,
|
|
228
|
+
user_id: opts.user_id,
|
|
229
|
+
agent_id: opts.agent_id,
|
|
230
|
+
limit: opts.limit ?? 10
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
return body.results ?? [];
|
|
234
|
+
}
|
|
235
|
+
/** GET /v1/memories — list a scope, newest first. */
|
|
236
|
+
async getAll(opts = {}) {
|
|
237
|
+
return this.request("GET", "/v1/memories", {
|
|
238
|
+
params: {
|
|
239
|
+
user_id: opts.user_id,
|
|
240
|
+
agent_id: opts.agent_id,
|
|
241
|
+
limit: opts.limit ?? 50,
|
|
242
|
+
offset: opts.offset ?? 0
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/** GET /v1/memories/:id — full content, metadata, extracted facts. */
|
|
247
|
+
async get(memoryId) {
|
|
248
|
+
return this.request("GET", `/v1/memories/${memoryId}`);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* PATCH /v1/memories/:id — re-runs extraction. Pass `expected_updated_at`
|
|
252
|
+
* for optimistic concurrency (throws StaleWriteError instead of clobbering).
|
|
253
|
+
*/
|
|
254
|
+
async update(memoryId, opts) {
|
|
255
|
+
return this.request("PATCH", `/v1/memories/${memoryId}`, {
|
|
256
|
+
body: {
|
|
257
|
+
content: opts.content,
|
|
258
|
+
expected_updated_at: opts.expected_updated_at
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/** DELETE /v1/memories/:id — forget one memory (audited invalidation). */
|
|
263
|
+
async delete(memoryId) {
|
|
264
|
+
return this.request("DELETE", `/v1/memories/${memoryId}`);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* DELETE /v1/users/:user_id/memories — forget every memory + fact for one
|
|
268
|
+
* end user in a single call.
|
|
269
|
+
*/
|
|
270
|
+
async deleteAll(opts) {
|
|
271
|
+
return this.request(
|
|
272
|
+
"DELETE",
|
|
273
|
+
`/v1/users/${opts.user_id}/memories`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* GET /v1/memories/:id/history — the lifecycle timeline of a memory:
|
|
278
|
+
* created / updated / deleted, plus every typed fact it produced.
|
|
279
|
+
*/
|
|
280
|
+
async history(memoryId) {
|
|
281
|
+
return this.request("GET", `/v1/memories/${memoryId}/history`);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* GET /v1/users — the end users you've stored data for (distinct user_id
|
|
285
|
+
* namespaces), each with active memory + fact counts and last-active time.
|
|
286
|
+
*/
|
|
287
|
+
async users(opts = {}) {
|
|
288
|
+
return this.request("GET", "/v1/users", {
|
|
289
|
+
params: {
|
|
290
|
+
agent_id: opts.agent_id,
|
|
291
|
+
limit: opts.limit ?? 50,
|
|
292
|
+
offset: opts.offset ?? 0
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
// ── facts ─────────────────────────────────────────────────────────────────
|
|
297
|
+
/**
|
|
298
|
+
* GET /v1/facts — typed (subject, predicate, object) triples with bi-temporal
|
|
299
|
+
* validity. Pass `as_of` (ISO date) for a point-in-time query.
|
|
300
|
+
*/
|
|
301
|
+
async getFacts(opts = {}) {
|
|
302
|
+
const params = {
|
|
303
|
+
subject: opts.subject,
|
|
304
|
+
entity: opts.entity,
|
|
305
|
+
predicate: opts.predicate,
|
|
306
|
+
predicate_family: opts.predicate_family,
|
|
307
|
+
as_of: opts.as_of,
|
|
308
|
+
user_id: opts.user_id,
|
|
309
|
+
agent_id: opts.agent_id,
|
|
310
|
+
limit: opts.limit ?? 50,
|
|
311
|
+
offset: opts.offset ?? 0
|
|
312
|
+
};
|
|
313
|
+
if (opts.include_invalidated) params.include_invalidated = "true";
|
|
314
|
+
const body = await this.request("GET", "/v1/facts", { params });
|
|
315
|
+
return body.facts ?? [];
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* POST /v1/facts — write a typed (subject, predicate, object) triple directly,
|
|
319
|
+
* skipping extraction. The server runs the contradiction check; the fact is
|
|
320
|
+
* bi-temporal (pass `valid_from` for a historical fact). Resolves to the
|
|
321
|
+
* written Fact, with `invalidated` listing any fact ids it superseded.
|
|
322
|
+
*/
|
|
323
|
+
async addFactTriple(subject, predicate, object, opts = {}) {
|
|
324
|
+
return this.request("POST", "/v1/facts", {
|
|
325
|
+
body: {
|
|
326
|
+
subject,
|
|
327
|
+
predicate,
|
|
328
|
+
object,
|
|
329
|
+
user_id: opts.user_id,
|
|
330
|
+
agent_id: opts.agent_id,
|
|
331
|
+
run_id: opts.run_id,
|
|
332
|
+
subject_type: opts.subject_type ?? "unknown",
|
|
333
|
+
object_is_literal: opts.object_is_literal ?? false,
|
|
334
|
+
confidence: opts.confidence ?? 0.9,
|
|
335
|
+
valid_from: opts.valid_from
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* GET /v1/profile — the assembled profile of one end user: the active typed
|
|
341
|
+
* facts known about them, the end user's own facts first, grouped by family.
|
|
342
|
+
* Pass `as_of` (ISO date) for the point-in-time profile.
|
|
343
|
+
*/
|
|
344
|
+
async getProfile(opts) {
|
|
345
|
+
return this.request("GET", "/v1/profile", {
|
|
346
|
+
params: {
|
|
347
|
+
user_id: opts.user_id,
|
|
348
|
+
agent_id: opts.agent_id,
|
|
349
|
+
as_of: opts.as_of
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
// ── context ─────────────────────────────────────────────────────────────
|
|
354
|
+
/**
|
|
355
|
+
* GET /v1/context — one call that assembles a prompt-ready context block
|
|
356
|
+
* (profile + relevant facts + memories) within a token budget.
|
|
357
|
+
*/
|
|
358
|
+
async getContext(opts) {
|
|
359
|
+
return this.request("GET", "/v1/context", {
|
|
360
|
+
params: {
|
|
361
|
+
query: opts.query,
|
|
362
|
+
user_id: opts.user_id,
|
|
363
|
+
agent_id: opts.agent_id,
|
|
364
|
+
token_budget: opts.token_budget ?? 800
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
// ── batch ─────────────────────────────────────────────────────────────────
|
|
369
|
+
/** POST /v1/batch — bulk import (up to 500 memory objects), async. */
|
|
370
|
+
async batch(memories) {
|
|
371
|
+
return this.request("POST", "/v1/batch", { body: { memories } });
|
|
372
|
+
}
|
|
373
|
+
/** GET /v1/batch/:id — poll an import job. */
|
|
374
|
+
async batchStatus(jobId) {
|
|
375
|
+
return this.request("GET", `/v1/batch/${jobId}`);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
379
|
+
0 && (module.exports = {
|
|
380
|
+
APIError,
|
|
381
|
+
AuthenticationError,
|
|
382
|
+
Korely,
|
|
383
|
+
KorelyError,
|
|
384
|
+
NamespaceForbiddenError,
|
|
385
|
+
NotFoundError,
|
|
386
|
+
QuotaExceededError,
|
|
387
|
+
StaleWriteError,
|
|
388
|
+
VERSION
|
|
389
|
+
});
|