@persistmemory/sdk 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PersistMemory
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,520 @@
1
+ # @persistmemory/sdk
2
+
3
+ The official TypeScript client for the **PersistMemory** API — memory that persists across every
4
+ model, tool and session you use.
5
+
6
+ [![npm](https://img.shields.io/npm/v/@persistmemory/sdk.svg)](https://www.npmjs.com/package/@persistmemory/sdk)
7
+ [![node](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org)
8
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
9
+
10
+ - **Typed end to end** — every request and response, including the search explanation
11
+ - **Zero dependencies** — ESM and CommonJS builds, nothing transitive
12
+ - **Retries that are safe** — GETs retry with jittered backoff; POSTs only with an idempotency key
13
+ - **Cursor pagination as an async iterator** — `for await (const memory of client.memories.list())`
14
+ - **Errors you can branch on** — `NotFoundError`, `RateLimitError`, `ValidationError`, and more
15
+ - **Injectable `fetch`** — your tests cannot open a socket by accident
16
+
17
+ ---
18
+
19
+ ## Table of contents
20
+
21
+ 1. [Install](#install)
22
+ 2. [Get an API key](#get-an-api-key)
23
+ 3. [Quick start](#quick-start)
24
+ 4. [Spaces](#spaces)
25
+ 5. [Memories](#memories)
26
+ 6. [Search](#search)
27
+ 7. [Context assembly](#context-assembly)
28
+ 8. [Pagination](#pagination)
29
+ 9. [Errors](#errors)
30
+ 10. [Retries, timeouts and idempotency](#retries-timeouts-and-idempotency)
31
+ 11. [Configuration reference](#configuration-reference)
32
+ 12. [Testing](#testing)
33
+ 13. [Troubleshooting](#troubleshooting)
34
+ 14. [FAQ](#faq)
35
+
36
+ ---
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ npm install @persistmemory/sdk
42
+ # or
43
+ yarn add @persistmemory/sdk
44
+ # or
45
+ pnpm add @persistmemory/sdk
46
+ ```
47
+
48
+ Node 20 or newer. Works in ESM and CommonJS:
49
+
50
+ ```ts
51
+ import { PersistMemory } from "@persistmemory/sdk"; // ESM
52
+ ```
53
+ ```js
54
+ const { PersistMemory } = require("@persistmemory/sdk"); // CommonJS
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Get an API key
60
+
61
+ The key is created on the website, and it is shown to you exactly once.
62
+
63
+ 1. **Create an account** at <https://persistmemory.com/signup>, or sign in at
64
+ <https://persistmemory.com/signin>. Google sign-in works too.
65
+ 2. **Open Settings.** Once you are signed in, *Settings* appears in the header —
66
+ or go straight to <https://persistmemory.com/settings#keys>.
67
+ 3. **Find “API keys”** and give the new key a name. Name it after *the thing that
68
+ will use it* — `billing-worker`, `laptop`, `staging` — not after yourself. The
69
+ name is how you decide which key to revoke when a laptop goes missing.
70
+ 4. **Press Create, then copy it immediately.** The key looks like
71
+ `pm_live_` followed by 43 characters. It is stored only as a hash, so nobody —
72
+ including us — can show it to you again. A lost key is revoked and replaced,
73
+ never recovered.
74
+ 5. **Put it in your environment**, never in your source:
75
+
76
+ ```bash
77
+ export PERSISTMEMORY_API_KEY=pm_live_...
78
+ ```
79
+
80
+ ```ts
81
+ const client = new PersistMemory({ apiKey: process.env.PERSISTMEMORY_API_KEY! });
82
+ ```
83
+
84
+ ### What a key can and cannot do
85
+
86
+ | A key can | A key cannot |
87
+ | --- | --- |
88
+ | Read and write your memories | Change your password |
89
+ | Create, update and archive your spaces | Create another API key |
90
+ | Run search and context assembly | Read anybody else's data |
91
+
92
+ That asymmetry is deliberate: a leaked key is something you revoke, not an account takeover.
93
+ Revoke one at any time in **Settings → API keys → Revoke**; anything using it stops working
94
+ immediately.
95
+
96
+ > **Never commit a key.** If one reaches a git history, a CI log or a screenshot, revoke it and
97
+ > create a new one. That takes fifteen seconds and is always the right move.
98
+
99
+ ---
100
+
101
+ ## Quick start
102
+
103
+ ```ts
104
+ import { PersistMemory } from "@persistmemory/sdk";
105
+
106
+ const client = new PersistMemory({
107
+ apiKey: process.env.PERSISTMEMORY_API_KEY!
108
+ });
109
+
110
+ // Capture something. This is asynchronous — see the note below.
111
+ await client.memories.remember({
112
+ text: "We chose Postgres for the ledger, not DynamoDB. Transactions across accounts."
113
+ });
114
+
115
+ // Ask for it back, in your own words.
116
+ const found = await client.search.query({
117
+ query: "what did we decide about the ledger database"
118
+ });
119
+
120
+ for (const result of found.results) {
121
+ console.log(result.score.toFixed(2), result.memory.title);
122
+ }
123
+ ```
124
+
125
+ > **`remember` returns a job, not a memory.** Extraction, entity resolution and consolidation
126
+ > run afterwards, and may produce one memory, several, or none — the same fact said twice
127
+ > produces one memory with two pieces of evidence. What comes back is
128
+ > `{ status, jobId, note }`. Poll `client.jobs` if you need to know when it has settled.
129
+
130
+ ---
131
+
132
+ ## Spaces
133
+
134
+ A **space** is a boundary around memories. Search inside one and you see only what is in it;
135
+ that is the whole point. Use spaces to separate work from personal, one client from another,
136
+ or a shared team memory from your own.
137
+
138
+ ### Create a space
139
+
140
+ ```ts
141
+ const work = await client.spaces.create({
142
+ name: "Work",
143
+ description: "Decisions, commitments and context for my job",
144
+ kind: "project"
145
+ });
146
+
147
+ console.log(work.id); // pass this to remember/search
148
+ ```
149
+
150
+ ### Kinds
151
+
152
+ | `kind` | Use it for |
153
+ | --- | --- |
154
+ | `user` | Your own personal memory (the default home for anything uncategorised) |
155
+ | `project` | One project, client or repository |
156
+ | `organizational` | A team or company space, shared by several people |
157
+ | `temporary` | Short-lived context — a trip, an incident, a sprint |
158
+ | `system` | Reserved for spaces PersistMemory manages for you |
159
+
160
+ ### Multiple spaces
161
+
162
+ Nothing stops you having many, and a memory can belong to more than one.
163
+
164
+ ```ts
165
+ const [work, acme, personal] = await Promise.all([
166
+ client.spaces.create({ name: "Work", kind: "project" }),
167
+ client.spaces.create({ name: "Acme migration", kind: "project", parentId: undefined }),
168
+ client.spaces.create({ name: "Personal", kind: "user" })
169
+ ]);
170
+
171
+ // Write into two at once.
172
+ await client.memories.remember({
173
+ text: "Acme's cutover is the 14th; Priya signs off.",
174
+ spaceIds: [work.id, acme.id]
175
+ });
176
+
177
+ // Read from one.
178
+ const inAcme = await client.search.query({
179
+ query: "when is the cutover",
180
+ spaceIds: [acme.id],
181
+ scope: "space"
182
+ });
183
+ ```
184
+
185
+ `scope` decides where search is allowed to look:
186
+
187
+ | `scope` | Meaning |
188
+ | --- | --- |
189
+ | `"space"` | Only the spaces you named. Nothing else can leak in. |
190
+ | `"universal"` | Everything you have, ignoring space boundaries. |
191
+ | `"combined"` | The named spaces first, then everything else, ranked together. |
192
+
193
+ ### Nesting, listing, updating, archiving
194
+
195
+ ```ts
196
+ // A child space — "Acme" under "Work".
197
+ const acme = await client.spaces.create({
198
+ name: "Acme",
199
+ kind: "project",
200
+ parentId: work.id
201
+ });
202
+
203
+ // List them all (paginated; see below).
204
+ for await (const space of client.spaces.list()) {
205
+ console.log(space.name, space.kind, space.memoryCount);
206
+ }
207
+
208
+ // Rename, or set a retention window.
209
+ await client.spaces.update(acme.id, { description: "Migration, phase 2", retentionDays: 365 });
210
+
211
+ // Archive rather than delete: the memories stay, the space stops appearing.
212
+ await client.spaces.update(acme.id, { archived: true });
213
+ ```
214
+
215
+ ### Moving memories between spaces
216
+
217
+ ```ts
218
+ await client.spaces.addMemories(acme.id, [id1, id2]);
219
+ await client.spaces.removeMemories(work.id, [id1]);
220
+
221
+ // Everything in one space.
222
+ for await (const memory of client.spaces.memories(acme.id)) {
223
+ console.log(memory.title);
224
+ }
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Memories
230
+
231
+ ```ts
232
+ // Capture. Optionally title it and file it into spaces.
233
+ const job = await client.memories.remember({
234
+ text: "Priya prefers async updates over standups.",
235
+ title: "Priya — working style",
236
+ spaceIds: [work.id]
237
+ });
238
+
239
+ // Read one.
240
+ const memory = await client.memories.get(memoryId);
241
+
242
+ // List, filtered by type.
243
+ for await (const decision of client.memories.list({ type: "decision" })) {
244
+ console.log(decision.title);
245
+ }
246
+ ```
247
+
248
+ Memory types: `fact`, `preference`, `decision`, `task`, `commitment`, `event`, and more —
249
+ the union is exported as `MemoryType`.
250
+
251
+ ---
252
+
253
+ ## Search
254
+
255
+ ```ts
256
+ const found = await client.search.query({
257
+ query: "what did we decide about the ledger",
258
+ types: ["decision"],
259
+ spaceIds: [work.id],
260
+ scope: "space",
261
+ limit: 10,
262
+ minScore: 0.4,
263
+ explain: true
264
+ });
265
+
266
+ if (found.diagnostics.degraded) {
267
+ // Embeddings were unavailable and this fell back to deterministic retrieval.
268
+ // The results are still real; the ranking is weaker. Worth surfacing.
269
+ console.warn("degraded:", found.diagnostics.unavailable);
270
+ }
271
+
272
+ for (const result of found.results) {
273
+ console.log(result.score, result.memory.title, result.explanation?.paths);
274
+ }
275
+ ```
276
+
277
+ **Read `diagnostics.degraded`.** It is not decoration. Search falls back rather than failing,
278
+ and a silent fallback that nobody checks is how a search quietly gets worse for a week.
279
+
280
+ ### Asking about the past
281
+
282
+ ```ts
283
+ // What did we believe on the 1st of March, not what we believe now?
284
+ const then = await client.search.query({
285
+ query: "our pricing",
286
+ asOf: "2026-03-01T00:00:00Z",
287
+ includeHistorical: true
288
+ });
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Context assembly
294
+
295
+ For putting memory into a prompt without blowing the window:
296
+
297
+ ```ts
298
+ const context = await client.search.context({
299
+ query: userMessage,
300
+ tokenBudget: 1500,
301
+ spaceIds: [work.id],
302
+ scope: "space"
303
+ });
304
+
305
+ const prompt = `${context.context}\n\nUser: ${userMessage}`;
306
+
307
+ console.log(context.usedTokens, "of 1500", context.truncated ? "(truncated)" : "");
308
+ ```
309
+
310
+ `context.context` is a formatted block, ready to paste into a system prompt.
311
+ `context.memories` tells you what went into it, so you can cite or debug it.
312
+
313
+ ---
314
+
315
+ ## Pagination
316
+
317
+ Every list endpoint returns a `Paginated<T>` — four ways to consume it, all lazy:
318
+
319
+ ```ts
320
+ // 1. Iterate every item, fetching pages as needed.
321
+ for await (const memory of client.memories.list({ type: "task" })) { … }
322
+
323
+ // 2. One page, for a UI that renders one page.
324
+ const page = await client.memories.list({ limit: 50 }).first();
325
+ page.data; // T[]
326
+ page.nextCursor; // undefined means there is no more
327
+
328
+ // 3. Page by page.
329
+ for await (const page of client.memories.list().pages()) { … }
330
+
331
+ // 4. Up to N items, with a hard ceiling so a bug cannot page forever.
332
+ const first200 = await client.memories.list().all(200);
333
+ ```
334
+
335
+ > `nextCursor` being absent is the **only** signal to stop. An empty `data` array is not the
336
+ > same thing — a page can come back empty because everything in it was filtered out.
337
+
338
+ ---
339
+
340
+ ## Errors
341
+
342
+ Every failure is an instance of `PersistMemoryError` with a subclass you can branch on:
343
+
344
+ ```ts
345
+ import {
346
+ PersistMemory,
347
+ NotFoundError,
348
+ RateLimitError,
349
+ ValidationError,
350
+ AuthenticationError,
351
+ PersistMemoryError
352
+ } from "@persistmemory/sdk";
353
+
354
+ try {
355
+ await client.memories.get(id);
356
+ } catch (error) {
357
+ if (error instanceof NotFoundError) return null;
358
+ if (error instanceof RateLimitError) {
359
+ await sleep((error.retryAfterSeconds ?? 1) * 1000);
360
+ return retry();
361
+ }
362
+ if (error instanceof AuthenticationError) throw new Error("Check PERSISTMEMORY_API_KEY");
363
+ if (error instanceof PersistMemoryError) {
364
+ console.error(error.status, error.code, error.requestId);
365
+ }
366
+ throw error;
367
+ }
368
+ ```
369
+
370
+ | Class | HTTP | Means |
371
+ | --- | --- | --- |
372
+ | `AuthenticationError` | 401 | Missing, malformed or revoked key |
373
+ | `PermissionDeniedError` | 403 | The key is valid but not allowed to do this |
374
+ | `NotFoundError` | 404 | No such memory, space or job |
375
+ | `ValidationError` | 422 | The request shape was wrong — read `.message` |
376
+ | `ConflictError` | 409 | Something changed underneath you |
377
+ | `RateLimitError` | 429 | Slow down; carries `retryAfterSeconds` |
378
+ | `ServerError` | 5xx | Ours. Retried automatically for GETs |
379
+ | `ConnectionError` | — | The request never reached us |
380
+ | `TimeoutError` | — | It reached us and did not come back in time |
381
+ | `AbortError` | — | You aborted it via `signal` |
382
+
383
+ Every error carries `requestId` — quote it if you contact support, it is how a request is
384
+ found in our logs.
385
+
386
+ **The key never appears in an error, a log line, or a stringified client.**
387
+ `JSON.stringify(client)` prints `{ baseUrl, apiKey: "[redacted]" }`, and so does
388
+ `console.log(client)`.
389
+
390
+ ---
391
+
392
+ ## Retries, timeouts and idempotency
393
+
394
+ GETs are retried with jittered exponential backoff. **POSTs are not** — a POST that timed out
395
+ may already have been processed, and this client will not guess. Opt in per request when the
396
+ operation is safe to repeat:
397
+
398
+ ```ts
399
+ await client.memories.remember(
400
+ { text: "…" },
401
+ { idempotencyKey: crypto.randomUUID(), maxAttempts: 3 }
402
+ );
403
+ ```
404
+
405
+ Per-request overrides:
406
+
407
+ ```ts
408
+ await client.search.query(
409
+ { query: "…" },
410
+ { timeoutMs: 5_000, signal: controller.signal }
411
+ );
412
+ ```
413
+
414
+ ---
415
+
416
+ ## Configuration reference
417
+
418
+ ```ts
419
+ const client = new PersistMemory({
420
+ apiKey: process.env.PERSISTMEMORY_API_KEY!, // required
421
+ baseUrl: "https://api.persistmemory.com", // default
422
+ timeoutMs: 30_000,
423
+ maxAttempts: 3,
424
+ backoff: { baseMs: 250, maxMs: 8_000, factor: 2, jitter: 0.2 },
425
+ userAgent: "my-app/1.2.3",
426
+ fetch: myFetch // injectable, see Testing
427
+ });
428
+ ```
429
+
430
+ | Option | Default | Notes |
431
+ | --- | --- | --- |
432
+ | `apiKey` | — | Required. From <https://persistmemory.com/settings#keys> |
433
+ | `baseUrl` | `https://api.persistmemory.com` | Trailing slashes are stripped for you |
434
+ | `timeoutMs` | `30000` | Per attempt, not per call |
435
+ | `maxAttempts` | `3` | GETs only, unless you pass `idempotencyKey` |
436
+ | `backoff` | jittered | `DEFAULT_BACKOFF` is exported if you want to read it |
437
+ | `fetch` | `globalThis.fetch` | Anything with the same signature |
438
+ | `userAgent` | SDK name and version | Yours is appended, not substituted |
439
+
440
+ Environment variables the SDK itself reads: **none.** It takes what you pass it, so a test
441
+ cannot pick up a real key from your shell by accident. Read `process.env` yourself, once,
442
+ where you construct the client.
443
+
444
+ ---
445
+
446
+ ## Testing
447
+
448
+ Inject a `fetch` and your test can never reach the network:
449
+
450
+ ```ts
451
+ const client = new PersistMemory({
452
+ apiKey: "pm_live_test",
453
+ fetch: async (url, init) => {
454
+ expect(String(url)).toContain("/api/v1/search");
455
+ return new Response(JSON.stringify({ results: [], diagnostics: { steps: [], degraded: false, tookMs: 1 }, query: "x" }), {
456
+ status: 200,
457
+ headers: { "content-type": "application/json" }
458
+ });
459
+ }
460
+ });
461
+ ```
462
+
463
+ `sleep` is injectable too, so a retry test does not spend real seconds waiting.
464
+
465
+ ---
466
+
467
+ ## Troubleshooting
468
+
469
+ **`AuthenticationError: 401`**
470
+ The key is missing, mistyped, or revoked. Check `process.env.PERSISTMEMORY_API_KEY` is actually
471
+ set in the process you are running (a `.env` file is not read for you), and that it starts with
472
+ `pm_live_`. If in doubt, create a fresh key — it costs nothing.
473
+
474
+ **`remember` succeeded but `search` finds nothing**
475
+ `remember` is asynchronous. Extraction runs after the call returns, so there is a short window
476
+ where the job exists and the memory does not. Poll the job id, or search a moment later.
477
+
478
+ **`search` returns fewer results than expected**
479
+ Check `scope`. With `scope: "space"` and a `spaceIds` list, anything outside those spaces is
480
+ invisible by design. Try `scope: "combined"`.
481
+
482
+ **`diagnostics.degraded` is `true`**
483
+ Embeddings were unavailable and search fell back to deterministic retrieval. Results are real
484
+ but ranked more weakly. Usually transient.
485
+
486
+ **Rate limited**
487
+ `RateLimitError.retryAfterSeconds` comes from the `Retry-After` header. Wait that long, not a
488
+ guess of your own.
489
+
490
+ ---
491
+
492
+ ## FAQ
493
+
494
+ **Does it work in the browser?**
495
+ Technically yes, but do not. An API key in a browser is a key you have given away — every
496
+ visitor can read it. Call your own server, and let the server hold the key.
497
+
498
+ **Does it work in Cloudflare Workers / Deno / Bun?**
499
+ Yes. The package is dependency-free and uses only `fetch` and standard globals. Pass your
500
+ runtime's `fetch` explicitly if it is not on `globalThis`.
501
+
502
+ **Is there a Python client?**
503
+ Yes — `pip install persistmemory`. Same endpoints, same retry policy, same paginator.
504
+
505
+ **How do I use this from a terminal instead?**
506
+ `npm install -g @persistmemory/cli`, then `pm auth login`. It signs in through your browser
507
+ with OAuth, so no key is needed at all.
508
+
509
+ ---
510
+
511
+ ## Links
512
+
513
+ - Documentation — <https://persistmemory.com/docs/sdk>
514
+ - API keys — <https://persistmemory.com/settings#keys>
515
+ - CLI — <https://persistmemory.com/docs/cli>
516
+ - Support — <mailto:hello@persistmemory.com>
517
+
518
+ ## License
519
+
520
+ MIT © PersistMemory
@@ -0,0 +1,73 @@
1
+ /**
2
+ * How long to wait before trying again.
3
+ *
4
+ * Exponential, capped, and jittered, for the reasons the rest of this repo
5
+ * gives in `@persistmemory/async`:
6
+ *
7
+ * exponential a cause that has not cleared in 200ms may clear in two
8
+ * seconds; hammering it every 200ms spends the attempts faster
9
+ * without giving it time to recover.
10
+ *
11
+ * capped unbounded doubling reaches minutes, and a caller waiting
12
+ * inside one function call cannot tell that from a hang.
13
+ *
14
+ * jittered the one that gets skipped, and the one that matters most. An
15
+ * outage fails every in-flight request at once; without jitter
16
+ * every client waits exactly two seconds and retries in the
17
+ * same millisecond, so the recovering service is hit by the
18
+ * whole fleet and knocked over again. The retry storm is caused
19
+ * by the retry policy.
20
+ *
21
+ * Duplicated here rather than imported from `@persistmemory/async` on purpose:
22
+ * this package is published to npm and installed by people who have no reason
23
+ * to pull in a queue runtime, a Redis client and a dead-letter store to make
24
+ * an HTTP request.
25
+ */
26
+ export interface BackoffOptions {
27
+ readonly baseMs?: number;
28
+ readonly maxMs?: number;
29
+ readonly factor?: number;
30
+ /** 0 = none, 1 = full. Full is the right default; see below. */
31
+ readonly jitter?: number;
32
+ /** Injected so a test is not at the mercy of the random number generator. */
33
+ readonly random?: () => number;
34
+ }
35
+ /**
36
+ * Short by server standards.
37
+ *
38
+ * A queue worker can afford to come back in a minute. A caller is blocked
39
+ * inside `await client.search(...)` and has a user watching, so the whole
40
+ * retry budget has to fit inside a request timeout rather than outlast it.
41
+ */
42
+ export declare const DEFAULT_BACKOFF: {
43
+ readonly baseMs: 250;
44
+ readonly maxMs: 8000;
45
+ readonly factor: 2;
46
+ readonly jitter: 1;
47
+ };
48
+ /**
49
+ * The delay before attempt `attempt` (1-based).
50
+ *
51
+ * FULL jitter by default - a random point in [0, ceiling] rather than
52
+ * ceiling plus or minus a wobble. It sounds worse and measures better:
53
+ * partial jitter leaves the retries clustered around the same instant, which
54
+ * is the thing that overwhelms a service coming back up.
55
+ */
56
+ export declare function backoffMs(attempt: number, options?: BackoffOptions): number;
57
+ /**
58
+ * The delay to actually use, honouring what the server asked for.
59
+ *
60
+ * `Retry-After` is taken as a FLOOR, not verbatim. Verbatim would let a
61
+ * one-second hint on the fifth consecutive 429 undo the backoff entirely and
62
+ * put us straight back into the limit; ignoring it would have us retry before
63
+ * the window resets and spend an attempt learning what we were already told.
64
+ *
65
+ * A server asking for LONGER than our cap is believed. The cap exists to stop
66
+ * our own growth running away, not to overrule a service that has told us
67
+ * when it will be ready.
68
+ */
69
+ export declare function delayFor(args: {
70
+ attempt: number;
71
+ retryAfterSeconds?: number;
72
+ options?: BackoffOptions;
73
+ }): number;
@@ -0,0 +1,46 @@
1
+ import type { ClientOptions, RequestOptions } from "./http.js";
2
+ import { Memories } from "./resources/memories.js";
3
+ import { Search } from "./resources/search.js";
4
+ import { Spaces } from "./resources/spaces.js";
5
+ import { Documents, Jobs, Sources } from "./resources/ingestion.js";
6
+ import { Conflicts, Entities, Graph } from "./resources/knowledge.js";
7
+ import { Conversations } from "./resources/conversations.js";
8
+ import { Integrations } from "./resources/integrations.js";
9
+ import { Health } from "./resources/health.js";
10
+ /**
11
+ * The client.
12
+ *
13
+ * const client = new PersistMemory({ apiKey: process.env.PERSISTMEMORY_API_KEY! });
14
+ * const found = await client.search.query({ query: "what did we decide about Postgres" });
15
+ *
16
+ * One transport underneath every resource, so retries, timeouts, error mapping
17
+ * and the credential are decided once. Resources are plain objects hanging off
18
+ * this one; they hold a reference to the transport and no state of their own,
19
+ * which is what makes a client safe to share across a process.
20
+ */
21
+ export declare class PersistMemory {
22
+ #private;
23
+ readonly memories: Memories;
24
+ readonly search: Search;
25
+ readonly spaces: Spaces;
26
+ readonly sources: Sources;
27
+ readonly documents: Documents;
28
+ readonly jobs: Jobs;
29
+ readonly entities: Entities;
30
+ readonly graph: Graph;
31
+ readonly conflicts: Conflicts;
32
+ readonly conversations: Conversations;
33
+ readonly integrations: Integrations;
34
+ readonly health: Health;
35
+ constructor(options: ClientOptions);
36
+ /**
37
+ * An escape hatch for an endpoint this package has not caught up with.
38
+ *
39
+ * Typed as `unknown` on purpose: a caller reaching past the typed surface is
40
+ * taking responsibility for the shape, and handing them `any` would let that
41
+ * responsibility spread silently through their codebase.
42
+ */
43
+ request<T = unknown>(method: "GET" | "POST" | "PATCH" | "DELETE", path: string, body?: unknown, options?: RequestOptions): Promise<T>;
44
+ /** Never the key. See `HttpClient.toJSON`, which this delegates to. */
45
+ toJSON(): Record<string, unknown>;
46
+ }