getmnemo 0.1.2 → 0.2.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/README.md CHANGED
@@ -6,7 +6,9 @@ Official TypeScript / JavaScript SDK for [Mnemo Memory](https://mnemohq.com) —
6
6
  npm install getmnemo
7
7
  ```
8
8
 
9
- Zero runtime dependencies. Works in Node 18+, Bun, Deno, browsers, Cloudflare Workers, and any other modern JS runtime with `fetch`.
9
+ Zero runtime dependencies. Works in Node 18+, Bun, Deno, Cloudflare Workers, and any other modern server or edge JS runtime with `fetch`.
10
+
11
+ A default `apiKey` is **full-access** — keep it server-side. Scoped keys **do** exist: the key-mint dialog lets you grant `read` / `write` / `delete` / `billing` scopes individually. For browser or client-exposed contexts, mint a **scoped read-only key** (or proxy through a server route) rather than shipping a full-access key.
10
12
 
11
13
  ## Quickstart
12
14
 
@@ -18,25 +20,59 @@ const memory = new Mnemo({
18
20
  workspaceId: process.env.GETMNEMO_WORKSPACE_ID!,
19
21
  })
20
22
 
21
- // Store an atomic fact
22
- await memory.add({ content: 'User prefers Japanese short-grain rice for onigiri.' })
23
+ // Store an atomic fact, scoped to a container (e.g. a user)
24
+ await memory.add({
25
+ content: 'User prefers Japanese short-grain rice for onigiri.',
26
+ containerTag: 'user:jane',
27
+ })
23
28
 
24
- // Retrieve relevant facts
25
- const { hits } = await memory.search({ query: 'what kind of rice does the user like?' })
26
- for (const hit of hits) {
29
+ // Retrieve relevant facts from the same container
30
+ const { results } = await memory.search({
31
+ q: 'what kind of rice does the user like?',
32
+ containerTag: 'user:jane',
33
+ })
34
+ for (const hit of results) {
27
35
  console.log(hit.score.toFixed(2), hit.content)
28
36
  }
29
37
  ```
30
38
 
39
+ ## Containers
40
+
41
+ Memories live in **containers**. Identify a container two ways:
42
+
43
+ - `containerTag` — a string like `"user:jane"` (the simplest, recommended form)
44
+ - `scope` — the structured equivalent `{ type: 'user', id: 'jane' }`
45
+
46
+ `add` and `search` require a container. Set one per call, or set `defaultContainerTag`
47
+ on the client once so every call falls back to it:
48
+
49
+ ```ts
50
+ const memory = new Mnemo({
51
+ apiKey: process.env.GETMNEMO_API_KEY!,
52
+ workspaceId: process.env.GETMNEMO_WORKSPACE_ID!,
53
+ defaultContainerTag: 'user:jane',
54
+ })
55
+
56
+ await memory.add({ content: 'Likes onigiri.' }) // uses user:jane
57
+ await memory.search({ q: 'food preferences?' }) // uses user:jane
58
+ ```
59
+
60
+ If neither a per-call container nor a default is set, `add`/`search` throw before
61
+ hitting the network.
62
+
31
63
  ## API surface
32
64
 
33
65
  | Method | Purpose |
34
66
  |---|---|
35
- | `search({ query, limit?, actorId? })` | Hybrid 7-strategy retrieval. Returns `SearchResponse`. |
36
- | `add({ content, metadata?, actorId? })` | Store an atomic fact. Returns `Memory`. |
37
- | `update(id, { content?, metadata? })` | Patch existing memory. |
38
- | `delete(id)` | Remove a memory. |
39
- | `list({ limit?, cursor?, actorId? })` | Cursor-paginated list. |
67
+ | `search({ q, containerTag?, scope?, limit?, searchMode? })` | Hybrid retrieval. Returns `SearchResponse`. |
68
+ | `add({ content, containerTag?, scope?, metadata? })` | Store an atomic fact. Returns `AddResponse`. |
69
+ | `update(memoryId, { content?, memoryType?, metadata?, source? })` | Patch an existing memory. |
70
+ | `get(memoryId)` | Fetch a single memory. |
71
+ | `delete(memoryId)` | Remove a memory. |
72
+ | `list({ containerTag?, limit?, cursor?, scopeType?, scopeId? })` | Cursor-paginated list. |
73
+
74
+ > Response types (`SearchResponse`, `AddResponse`, `Memory`, …) are **provisional** —
75
+ > reconstructed from observed live payloads pending a fully-annotated API spec.
40
76
 
41
77
  ## Errors
42
78
 
@@ -46,7 +82,7 @@ All HTTP failures throw `MnemoHTTPError` with `.status` and `.body`. Aborted req
46
82
  import { Mnemo, MnemoHTTPError } from 'getmnemo'
47
83
 
48
84
  try {
49
- await memory.search({ query: 'rice' })
85
+ await memory.search({ q: 'rice', containerTag: 'user:jane' })
50
86
  } catch (err) {
51
87
  if (err instanceof MnemoHTTPError && err.status === 401) {
52
88
  console.error('API key rejected:', err.body)
@@ -61,8 +97,8 @@ try {
61
97
  | Option | Default | Notes |
62
98
  |---|---|---|
63
99
  | `apiKey` | (required) | from <https://app.mnemohq.com/settings/api-keys> |
64
- | `workspaceId` | (required) | from the dashboard URL |
65
- | `actorId` | none | optional default actor scope |
100
+ | `workspaceId` | (required) | sent as the `x-workspace-id` header |
101
+ | `defaultContainerTag` | none | fallback container for `add`/`search` |
66
102
  | `baseUrl` | `https://api.mnemohq.com` | override for self-hosted |
67
103
  | `timeoutMs` | `30000` | per-request abort timeout |
68
104
  | `fetch` | global `fetch` | inject for testing or proxying |
@@ -75,6 +111,30 @@ npm test
75
111
  npm run build
76
112
  ```
77
113
 
114
+ ## CI smoke gate
115
+
116
+ The publish workflow (`.github/workflows/publish.yml`) will **not** publish to
117
+ npm unless a real production round-trip passes first. A `smoke` job runs on the
118
+ same `v*` tag trigger and the `publish` job depends on it via `needs: smoke`, so
119
+ a failed smoke blocks the release.
120
+
121
+ `npm run smoke` (`scripts/prod-smoke.mjs`) writes a memory to container **A** and
122
+ another to container **B** against prod, confirms the add/search round-trip via
123
+ `response.results`, then asserts **tenant isolation**: a search scoped to B must
124
+ not return A's memory, and vice versa. A leak exits non-zero with a loud
125
+ `TENANT ISOLATION FAILURE` — treat that as a production security finding, not a
126
+ flaky test. Created memories are deleted on cleanup (cleanup failure only warns).
127
+
128
+ It needs three secrets, which **must be ORG-level (no repo-level twin)** — a
129
+ repo-level twin shadows the org secret, which is exactly the failure mode that
130
+ broke an earlier publish:
131
+
132
+ | Secret | Purpose |
133
+ |---|---|
134
+ | `MNEMO_API_KEY` | Scoped test key. **Needs `delete` scope** so the smoke can clean up the memories it creates (plus `write` + `search`). |
135
+ | `MNEMO_WORKSPACE_ID` | Throwaway test workspace id. |
136
+ | `MNEMO_TEST_CONTAINER` | Base `containerTag` (e.g. `ci-smoke`); the script derives unique per-run A/B containers from it. |
137
+
78
138
  ## License
79
139
 
80
140
  MIT
package/dist/index.cjs CHANGED
@@ -30,8 +30,9 @@ var MnemoTimeoutError = class extends MnemoError {
30
30
  // src/client.ts
31
31
  var DEFAULT_BASE_URL = "https://api.mnemohq.com";
32
32
  var DEFAULT_TIMEOUT_MS = 3e4;
33
- var SDK_VERSION = "0.1.0";
34
- var USER_AGENT = `@mnemo/memory/${SDK_VERSION}`;
33
+ var SDK_VERSION = "0.2.0";
34
+ var DEFAULT_SEARCH_LIMIT = 8;
35
+ var USER_AGENT = `getmnemo/${SDK_VERSION}`;
35
36
  var DEFAULT_MAX_RETRIES = 3;
36
37
  var RETRY_BASE_DELAY_MS = 200;
37
38
  var RETRY_MAX_DELAY_MS = 5e3;
@@ -73,7 +74,7 @@ var Mnemo = class {
73
74
  #fetch;
74
75
  #timeoutMs;
75
76
  #maxRetries;
76
- #defaultActorId;
77
+ #defaultContainerTag;
77
78
  constructor(cfg) {
78
79
  if (!cfg.apiKey) throw new Error("Mnemo: apiKey is required");
79
80
  if (!cfg.workspaceId) throw new Error("Mnemo: workspaceId is required");
@@ -88,49 +89,104 @@ var Mnemo = class {
88
89
  } else {
89
90
  this.#headers["user-agent"] = USER_AGENT;
90
91
  }
91
- if (cfg.actorId) this.#headers["x-actor-id"] = cfg.actorId;
92
- this.#defaultActorId = cfg.actorId;
92
+ this.#defaultContainerTag = cfg.defaultContainerTag;
93
93
  this.#fetch = cfg.fetch ?? fetch;
94
94
  this.#timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS;
95
95
  this.#maxRetries = Math.max(0, cfg.maxRetries ?? DEFAULT_MAX_RETRIES);
96
96
  }
97
+ /**
98
+ * Resolve the container for a call into the request fields the API expects.
99
+ * A structured `scope` wins over a `containerTag` string; both fall back to
100
+ * the constructor's `defaultContainerTag`. Throws if none is available.
101
+ */
102
+ #resolveContainer(method, input) {
103
+ if (input.scope) return { scope: input.scope };
104
+ const tag = input.containerTag ?? this.#defaultContainerTag;
105
+ if (tag) return { containerTag: tag };
106
+ throw new Error(
107
+ `Mnemo.${method}: a container is required \u2014 pass containerTag (e.g. "user:jane") or scope ({ type, id }) per call, or set defaultContainerTag on the client.`
108
+ );
109
+ }
110
+ /**
111
+ * Hybrid retrieval. Requires a container — pass `containerTag` (e.g.
112
+ * `"user:jane"`) or `scope`, or set `defaultContainerTag` on the client.
113
+ *
114
+ * Sends `POST /v1/search` with body `{ q, limit, containerTag|scope }`.
115
+ */
97
116
  async search(input) {
117
+ const container = this.#resolveContainer("search", input);
98
118
  return this.#request("POST", "/v1/search", {
99
- query: input.query,
100
- limit: input.limit ?? 8,
101
- ...input.actorId !== void 0 ? { actorId: input.actorId } : {}
119
+ q: input.q,
120
+ limit: input.limit ?? DEFAULT_SEARCH_LIMIT,
121
+ ...input.searchMode !== void 0 ? { searchMode: input.searchMode } : {},
122
+ ...container
102
123
  });
103
124
  }
125
+ /**
126
+ * Store an atomic fact. Requires a container — pass `containerTag` (e.g.
127
+ * `"user:jane"`) or `scope`, or set `defaultContainerTag` on the client.
128
+ *
129
+ * Sends `POST /v1/memories` with body
130
+ * `{ items: [{ content, metadata? }], containerTag|scope }`.
131
+ */
104
132
  async add(input) {
133
+ const container = this.#resolveContainer("add", input);
105
134
  return this.#request("POST", "/v1/memories", {
106
- content: input.content,
107
- ...input.metadata !== void 0 ? { metadata: input.metadata } : {},
108
- ...input.actorId !== void 0 ? { actorId: input.actorId } : {}
135
+ items: [
136
+ {
137
+ content: input.content,
138
+ ...input.metadata !== void 0 ? { metadata: input.metadata } : {}
139
+ }
140
+ ],
141
+ ...container
109
142
  });
110
143
  }
111
- async update(id, input) {
112
- if (input.content === void 0 && input.metadata === void 0) {
113
- throw new Error("Mnemo.update: at least one of content/metadata must be provided");
144
+ /**
145
+ * Patch an existing memory by id.
146
+ * Sends `PATCH /v1/memories/{memoryId}` with body `UpdateMemoryDto`
147
+ * `{ content?, memoryType?, metadata?, source? }` (none required).
148
+ */
149
+ async update(memoryId, input) {
150
+ if (input.content === void 0 && input.memoryType === void 0 && input.metadata === void 0 && input.source === void 0) {
151
+ throw new Error(
152
+ "Mnemo.update: at least one of content/memoryType/metadata/source must be provided"
153
+ );
114
154
  }
115
- return this.#request("PATCH", `/v1/memories/${encodeURIComponent(id)}`, input);
155
+ return this.#request(
156
+ "PATCH",
157
+ `/v1/memories/${encodeURIComponent(memoryId)}`,
158
+ input
159
+ );
116
160
  }
117
- async get(id) {
118
- return this.#request("GET", `/v1/memories/${encodeURIComponent(id)}`);
161
+ /** Fetch a single memory by id. Sends `GET /v1/memories/{memoryId}`. */
162
+ async get(memoryId) {
163
+ return this.#request("GET", `/v1/memories/${encodeURIComponent(memoryId)}`);
119
164
  }
120
- async delete(id) {
121
- await this.#request("DELETE", `/v1/memories/${encodeURIComponent(id)}`);
165
+ /** Remove a memory by id. Sends `DELETE /v1/memories/{memoryId}`. */
166
+ async delete(memoryId) {
167
+ await this.#request(
168
+ "DELETE",
169
+ `/v1/memories/${encodeURIComponent(memoryId)}`
170
+ );
122
171
  }
172
+ /**
173
+ * Cursor-paginated list of memories, optionally filtered by container.
174
+ * Sends `GET /v1/memories` with query
175
+ * `limit?, cursor?, scopeType?, scopeId?, containerTag?`.
176
+ */
123
177
  async list(input) {
124
178
  const params = new URLSearchParams();
125
179
  if (input?.limit !== void 0) params.set("limit", String(input.limit));
126
180
  if (input?.cursor !== void 0) params.set("cursor", input.cursor);
127
- if (input?.actorId !== void 0) params.set("actorId", input.actorId);
181
+ if (input?.scopeType !== void 0) params.set("scopeType", input.scopeType);
182
+ if (input?.scopeId !== void 0) params.set("scopeId", input.scopeId);
183
+ if (input?.containerTag !== void 0) params.set("containerTag", input.containerTag);
128
184
  const qs = params.toString();
129
185
  return this.#request("GET", `/v1/memories${qs ? `?${qs}` : ""}`);
130
186
  }
131
187
  /** Echoed back for debugging — never sent to the wire. */
132
- get defaultActorId() {
133
- return this.#defaultActorId;
188
+ get defaultContainerTag() {
189
+ return this.#defaultContainerTag;
134
190
  }
135
191
  async #request(method, path, body) {
136
192
  const serializedBody = body === void 0 ? void 0 : JSON.stringify(body);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":";;;AAAO,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EACpC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF;AAEO,IAAM,cAAA,GAAN,cAA6B,UAAA,CAAW;AAAA,EAC7C,WAAA,CACE,OAAA,EACS,MAAA,EACA,IAAA,EACT;AACA,IAAA,KAAA,CAAM,CAAA,CAAA,EAAI,MAAM,CAAA,EAAA,EAAK,OAAO,CAAA,CAAE,CAAA;AAHrB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAGT,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AAAA,EANW,MAAA;AAAA,EACA,IAAA;AAMb;AAEO,IAAM,iBAAA,GAAN,cAAgC,UAAA,CAAW;AAAA,EAChD,YAAY,SAAA,EAAmB;AAC7B,IAAA,KAAA,CAAM,CAAA,wBAAA,EAA2B,SAAS,CAAA,EAAA,CAAI,CAAA;AAC9C,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF;;;ACEA,IAAM,gBAAA,GAAmB,yBAAA;AACzB,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,WAAA,GAAc,OAAA;AACpB,IAAM,UAAA,GAAa,iBAAiB,WAAW,CAAA,CAAA;AAC/C,IAAM,mBAAA,GAAsB,CAAA;AAC5B,IAAM,mBAAA,GAAsB,GAAA;AAC5B,IAAM,kBAAA,GAAqB,GAAA;AAI3B,IAAM,eAAA,GACJ,OAAO,UAAA,KAAe,WAAA;AAEtB,OAAQ,WAAmB,MAAA,KAAW,WAAA;AAEtC,OAAQ,WAAmB,QAAA,KAAa,WAAA;AAE1C,SAAS,aAAa,OAAA,EAAyB;AAC7C,EAAA,MAAM,SAAS,IAAA,CAAK,GAAA,CAAI,mBAAA,GAAsB,CAAA,IAAK,SAAS,kBAAkB,CAAA;AAE9E,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,MAAM,CAAA;AAC1C;AAEA,SAAS,kBAAkB,MAAA,EAAyB;AAElD,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,KAAA;AAC3B,EAAA,OAAO,MAAA,KAAW,GAAA,IAAQ,MAAA,IAAU,GAAA,IAAO,MAAA,GAAS,GAAA;AACtD;AAEA,SAAS,kBAAkB,WAAA,EAA2C;AACpE,EAAA,IAAI,CAAC,aAAa,OAAO,IAAA;AACzB,EAAA,MAAM,OAAA,GAAU,YAAY,IAAA,EAAK;AAEjC,EAAA,MAAM,OAAA,GAAU,OAAO,OAAO,CAAA;AAC9B,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,IAAK,WAAW,CAAA,EAAG;AAC5C,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,OAAA,GAAU,GAAA,EAAM,kBAAkB,CAAA;AAAA,EACpD;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAChC,EAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,KAAK,CAAA,EAAG;AACxB,IAAA,MAAM,KAAA,GAAQ,KAAA,GAAQ,IAAA,CAAK,GAAA,EAAI;AAC/B,IAAA,OAAO,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,KAAA,EAAO,kBAAkB,CAAC,CAAA;AAAA,EACxD;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,gBAAA,CAAiB,KAAe,OAAA,EAAyB;AAChE,EAAA,MAAM,OAAO,iBAAA,CAAkB,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAC,CAAA;AAC7D,EAAA,OAAO,IAAA,KAAS,IAAA,GAAO,IAAA,GAAO,YAAA,CAAa,OAAO,CAAA;AACpD;AAEA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;AAEO,IAAM,QAAN,MAAY;AAAA,EACR,QAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,eAAA;AAAA,EAET,YAAY,GAAA,EAAmB;AAC7B,IAAA,IAAI,CAAC,GAAA,CAAI,MAAA,EAAQ,MAAM,IAAI,MAAM,2BAA2B,CAAA;AAC5D,IAAA,IAAI,CAAC,GAAA,CAAI,WAAA,EAAa,MAAM,IAAI,MAAM,gCAAgC,CAAA;AACtE,IAAA,IAAA,CAAK,YAAY,GAAA,CAAI,OAAA,IAAW,gBAAA,EAAkB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACnE,IAAA,IAAA,CAAK,QAAA,GAAW;AAAA,MACd,aAAA,EAAe,CAAA,OAAA,EAAU,GAAA,CAAI,MAAM,CAAA,CAAA;AAAA,MACnC,kBAAkB,GAAA,CAAI,WAAA;AAAA,MACtB,cAAA,EAAgB;AAAA,KAClB;AAIA,IAAA,IAAI,eAAA,EAAiB;AACnB,MAAA,IAAA,CAAK,QAAA,CAAS,mBAAmB,CAAA,GAAI,UAAA;AAAA,IACvC,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,QAAA,CAAS,YAAY,CAAA,GAAI,UAAA;AAAA,IAChC;AACA,IAAA,IAAI,IAAI,OAAA,EAAS,IAAA,CAAK,QAAA,CAAS,YAAY,IAAI,GAAA,CAAI,OAAA;AACnD,IAAA,IAAA,CAAK,kBAAkB,GAAA,CAAI,OAAA;AAC3B,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,KAAA,IAAS,KAAA;AAC3B,IAAA,IAAA,CAAK,UAAA,GAAa,IAAI,SAAA,IAAa,kBAAA;AACnC,IAAA,IAAA,CAAK,cAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAA,CAAI,cAAc,mBAAmB,CAAA;AAAA,EACtE;AAAA,EAEA,MAAM,OAAO,KAAA,EAIe;AAC1B,IAAA,OAAO,IAAA,CAAK,QAAA,CAAyB,MAAA,EAAQ,YAAA,EAAc;AAAA,MACzD,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,KAAA,EAAO,MAAM,KAAA,IAAS,CAAA;AAAA,MACtB,GAAI,MAAM,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,EAAS,KAAA,CAAM,OAAA,EAAQ,GAAI;AAAC,KACjE,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,KAAA,EAIU;AAClB,IAAA,OAAO,IAAA,CAAK,QAAA,CAAiB,MAAA,EAAQ,cAAA,EAAgB;AAAA,MACnD,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,GAAI,MAAM,QAAA,KAAa,MAAA,GAAY,EAAE,QAAA,EAAU,KAAA,CAAM,QAAA,EAAS,GAAI,EAAC;AAAA,MACnE,GAAI,MAAM,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,EAAS,KAAA,CAAM,OAAA,EAAQ,GAAI;AAAC,KACjE,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,MAAA,CACJ,EAAA,EACA,KAAA,EACiB;AACjB,IAAA,IAAI,KAAA,CAAM,OAAA,KAAY,MAAA,IAAa,KAAA,CAAM,aAAa,MAAA,EAAW;AAC/D,MAAA,MAAM,IAAI,MAAM,iEAAiE,CAAA;AAAA,IACnF;AACA,IAAA,OAAO,IAAA,CAAK,SAAiB,OAAA,EAAS,CAAA,aAAA,EAAgB,mBAAmB,EAAE,CAAC,IAAI,KAAK,CAAA;AAAA,EACvF;AAAA,EAEA,MAAM,IAAI,EAAA,EAA6B;AACrC,IAAA,OAAO,KAAK,QAAA,CAAiB,KAAA,EAAO,gBAAgB,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAE,CAAA;AAAA,EAC9E;AAAA,EAEA,MAAM,OAAO,EAAA,EAA2B;AACtC,IAAA,MAAM,KAAK,QAAA,CAAkB,QAAA,EAAU,gBAAgB,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAE,CAAA;AAAA,EACjF;AAAA,EAEA,MAAM,KAAK,KAAA,EAIoB;AAC7B,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,IAAA,IAAI,KAAA,EAAO,UAAU,MAAA,EAAW,MAAA,CAAO,IAAI,OAAA,EAAS,MAAA,CAAO,KAAA,CAAM,KAAK,CAAC,CAAA;AACvE,IAAA,IAAI,OAAO,MAAA,KAAW,MAAA,SAAkB,GAAA,CAAI,QAAA,EAAU,MAAM,MAAM,CAAA;AAClE,IAAA,IAAI,OAAO,OAAA,KAAY,MAAA,SAAkB,GAAA,CAAI,SAAA,EAAW,MAAM,OAAO,CAAA;AACrE,IAAA,MAAM,EAAA,GAAK,OAAO,QAAA,EAAS;AAC3B,IAAA,OAAO,IAAA,CAAK,SAA4B,KAAA,EAAO,CAAA,YAAA,EAAe,KAAK,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,EAAE,CAAA,CAAE,CAAA;AAAA,EACpF;AAAA;AAAA,EAGA,IAAI,cAAA,GAAqC;AACvC,IAAA,OAAO,IAAA,CAAK,eAAA;AAAA,EACd;AAAA,EAEA,MAAM,QAAA,CAAY,MAAA,EAAgB,IAAA,EAAc,IAAA,EAA4B;AAC1E,IAAA,MAAM,iBAAiB,IAAA,KAAS,MAAA,GAAY,MAAA,GAAY,IAAA,CAAK,UAAU,IAAI,CAAA;AAC3E,IAAA,IAAI,OAAA;AACJ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,IAAA,CAAK,aAAa,OAAA,EAAA,EAAW;AAC5D,MAAA,MAAM,IAAA,GAAO,IAAI,eAAA,EAAgB;AACjC,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,KAAK,KAAA,EAAM,EAAG,KAAK,UAAU,CAAA;AAC5D,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,GAAG,IAAA,CAAK,QAAQ,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI;AAAA,UACvD,MAAA;AAAA,UACA,OAAA,EAAS,EAAE,GAAG,IAAA,CAAK,QAAA,EAAS;AAAA,UAC5B,IAAA,EAAM,cAAA;AAAA,UACN,QAAQ,IAAA,CAAK;AAAA,SACd,CAAA;AACD,QAAA,IAAI,kBAAkB,GAAA,CAAI,MAAM,CAAA,IAAK,OAAA,GAAU,KAAK,WAAA,EAAa;AAG/D,UAAA,MAAM,IAAA,GAAO,gBAAA,CAAiB,GAAA,EAAK,OAAO,CAAA;AAE1C,UAAA,MAAM,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,KAAA,CAAS,CAAA;AACtC,UAAA,MAAM,MAAM,IAAI,CAAA;AAChB,UAAA;AAAA,QACF;AACA,QAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,QAAA,MAAM,MAAA,GAAkB,IAAA,GAAO,QAAA,CAAS,IAAI,CAAA,GAAI,KAAA,CAAA;AAChD,QAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,UAAA,MAAM,WACH,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,aAAa,MAAA,GAClD,MAAA,CAAQ,MAAA,CAAgC,OAAO,IAC/C,IAAA,KAAS,CAAA,KAAA,EAAQ,IAAI,MAAM,CAAA,CAAA,EAAI,IAAI,UAAU,CAAA,CAAA;AACnD,UAAA,MAAM,IAAI,cAAA,CAAe,OAAA,EAAS,GAAA,CAAI,QAAQ,MAAM,CAAA;AAAA,QACtD;AACA,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,IAAI,GAAA,YAAe,KAAA,IAAS,GAAA,CAAI,IAAA,KAAS,YAAA,EAAc;AACrD,UAAA,MAAM,IAAI,iBAAA,CAAkB,IAAA,CAAK,UAAU,CAAA;AAAA,QAC7C;AACA,QAAA,IAAI,GAAA,YAAe,gBAAgB,MAAM,GAAA;AACzC,QAAA,OAAA,GAAU,GAAA;AACV,QAAA,IAAI,OAAA,GAAU,KAAK,WAAA,EAAa;AAC9B,UAAA,MAAM,KAAA,CAAM,YAAA,CAAa,OAAO,CAAC,CAAA;AACjC,UAAA;AAAA,QACF;AACA,QAAA,MAAM,GAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF;AACA,IAAA,MAAM,OAAA,IAAW,IAAI,KAAA,CAAM,uBAAuB,CAAA;AAAA,EACpD;AACF;AAEA,SAAS,SAAS,CAAA,EAAoB;AACpC,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,CAAA;AAAA,EACT;AACF","file":"index.cjs","sourcesContent":["export class MnemoError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MnemoError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class MnemoHTTPError extends MnemoError {\n constructor(\n message: string,\n readonly status: number,\n readonly body?: unknown,\n ) {\n super(`[${status}] ${message}`)\n this.name = 'MnemoHTTPError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class MnemoTimeoutError extends MnemoError {\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`)\n this.name = 'MnemoTimeoutError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n","/**\n * Mnemo Memory client.\n *\n * Zero runtime dependencies — uses the global `fetch` (Node 18+, Bun, browsers,\n * Cloudflare Workers, Deno, etc).\n *\n * @example\n * ```ts\n * import { Mnemo } from '@mnemo/memory'\n *\n * const memory = new Mnemo({\n * apiKey: process.env.GETMNEMO_API_KEY!,\n * workspaceId: process.env.GETMNEMO_WORKSPACE_ID!,\n * })\n *\n * await memory.add({ content: 'User prefers Japanese rice.' })\n * const { hits } = await memory.search({ query: 'what rice does the user like?' })\n * ```\n */\n\nimport { MnemoHTTPError, MnemoTimeoutError } from './errors.js'\nimport type {\n ClientConfig,\n Memory,\n PaginatedMemories,\n SearchResponse,\n} from './types.js'\n\nconst DEFAULT_BASE_URL = 'https://api.mnemohq.com'\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst SDK_VERSION = '0.1.0'\nconst USER_AGENT = `@mnemo/memory/${SDK_VERSION}`\nconst DEFAULT_MAX_RETRIES = 3\nconst RETRY_BASE_DELAY_MS = 200\nconst RETRY_MAX_DELAY_MS = 5_000\n\n// Browsers reject `user-agent` as a forbidden header — setting it via fetch\n// throws or warns. Detect a browser-like environment so we can skip it there.\nconst IS_BROWSER_LIKE =\n typeof globalThis !== 'undefined' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (globalThis as any).window !== 'undefined' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (globalThis as any).document !== 'undefined'\n\nfunction retryDelayMs(attempt: number): number {\n const capped = Math.min(RETRY_BASE_DELAY_MS * 2 ** attempt, RETRY_MAX_DELAY_MS)\n // Full jitter.\n return Math.floor(Math.random() * capped)\n}\n\nfunction isRetryableStatus(status: number): boolean {\n // 501 Not Implemented is a permanent failure — retrying just wastes round-trips.\n if (status === 501) return false\n return status === 429 || (status >= 500 && status < 600)\n}\n\nfunction parseRetryAfterMs(headerValue: string | null): number | null {\n if (!headerValue) return null\n const trimmed = headerValue.trim()\n // Delta-seconds form.\n const seconds = Number(trimmed)\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(seconds * 1000, RETRY_MAX_DELAY_MS)\n }\n // HTTP-date form.\n const epoch = Date.parse(trimmed)\n if (!Number.isNaN(epoch)) {\n const delta = epoch - Date.now()\n return Math.max(0, Math.min(delta, RETRY_MAX_DELAY_MS))\n }\n return null\n}\n\nfunction delayForResponse(res: Response, attempt: number): number {\n const hint = parseRetryAfterMs(res.headers.get('retry-after'))\n return hint !== null ? hint : retryDelayMs(attempt)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport class Mnemo {\n readonly #baseUrl: string\n readonly #headers: Record<string, string>\n readonly #fetch: typeof fetch\n readonly #timeoutMs: number\n readonly #maxRetries: number\n readonly #defaultActorId: string | undefined\n\n constructor(cfg: ClientConfig) {\n if (!cfg.apiKey) throw new Error('Mnemo: apiKey is required')\n if (!cfg.workspaceId) throw new Error('Mnemo: workspaceId is required')\n this.#baseUrl = (cfg.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.#headers = {\n authorization: `Bearer ${cfg.apiKey}`,\n 'x-workspace-id': cfg.workspaceId,\n 'content-type': 'application/json',\n }\n // `user-agent` is on the forbidden header list in browsers — setting it\n // via fetch is silently dropped or throws. Send `x-getmnemo-client` as\n // an SDK identifier in browsers, and the standard User-Agent on Node.\n if (IS_BROWSER_LIKE) {\n this.#headers['x-getmnemo-client'] = USER_AGENT\n } else {\n this.#headers['user-agent'] = USER_AGENT\n }\n if (cfg.actorId) this.#headers['x-actor-id'] = cfg.actorId\n this.#defaultActorId = cfg.actorId\n this.#fetch = cfg.fetch ?? fetch\n this.#timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS\n this.#maxRetries = Math.max(0, cfg.maxRetries ?? DEFAULT_MAX_RETRIES)\n }\n\n async search(input: {\n query: string\n limit?: number\n actorId?: string\n }): Promise<SearchResponse> {\n return this.#request<SearchResponse>('POST', '/v1/search', {\n query: input.query,\n limit: input.limit ?? 8,\n ...(input.actorId !== undefined ? { actorId: input.actorId } : {}),\n })\n }\n\n async add(input: {\n content: string\n metadata?: Record<string, unknown>\n actorId?: string\n }): Promise<Memory> {\n return this.#request<Memory>('POST', '/v1/memories', {\n content: input.content,\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n ...(input.actorId !== undefined ? { actorId: input.actorId } : {}),\n })\n }\n\n async update(\n id: string,\n input: { content?: string; metadata?: Record<string, unknown> },\n ): Promise<Memory> {\n if (input.content === undefined && input.metadata === undefined) {\n throw new Error('Mnemo.update: at least one of content/metadata must be provided')\n }\n return this.#request<Memory>('PATCH', `/v1/memories/${encodeURIComponent(id)}`, input)\n }\n\n async get(id: string): Promise<Memory> {\n return this.#request<Memory>('GET', `/v1/memories/${encodeURIComponent(id)}`)\n }\n\n async delete(id: string): Promise<void> {\n await this.#request<unknown>('DELETE', `/v1/memories/${encodeURIComponent(id)}`)\n }\n\n async list(input?: {\n limit?: number\n cursor?: string\n actorId?: string\n }): Promise<PaginatedMemories> {\n const params = new URLSearchParams()\n if (input?.limit !== undefined) params.set('limit', String(input.limit))\n if (input?.cursor !== undefined) params.set('cursor', input.cursor)\n if (input?.actorId !== undefined) params.set('actorId', input.actorId)\n const qs = params.toString()\n return this.#request<PaginatedMemories>('GET', `/v1/memories${qs ? `?${qs}` : ''}`)\n }\n\n /** Echoed back for debugging — never sent to the wire. */\n get defaultActorId(): string | undefined {\n return this.#defaultActorId\n }\n\n async #request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const serializedBody = body === undefined ? undefined : JSON.stringify(body)\n let lastErr: unknown\n for (let attempt = 0; attempt <= this.#maxRetries; attempt++) {\n const ctrl = new AbortController()\n const timer = setTimeout(() => ctrl.abort(), this.#timeoutMs)\n try {\n const res = await this.#fetch(`${this.#baseUrl}${path}`, {\n method,\n headers: { ...this.#headers },\n body: serializedBody,\n signal: ctrl.signal,\n })\n if (isRetryableStatus(res.status) && attempt < this.#maxRetries) {\n // Capture Retry-After before draining; some runtimes invalidate\n // headers once the body is consumed.\n const wait = delayForResponse(res, attempt)\n // Drain body so the underlying connection can be reused.\n await res.text().catch(() => undefined)\n await sleep(wait)\n continue\n }\n const text = await res.text()\n const parsed: unknown = text ? safeJson(text) : undefined\n if (!res.ok) {\n const message =\n (parsed && typeof parsed === 'object' && 'message' in parsed\n ? String((parsed as { message: unknown }).message)\n : null) ?? `HTTP ${res.status} ${res.statusText}`\n throw new MnemoHTTPError(message, res.status, parsed)\n }\n return parsed as T\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new MnemoTimeoutError(this.#timeoutMs)\n }\n if (err instanceof MnemoHTTPError) throw err\n lastErr = err\n if (attempt < this.#maxRetries) {\n await sleep(retryDelayMs(attempt))\n continue\n }\n throw err\n } finally {\n clearTimeout(timer)\n }\n }\n throw lastErr ?? new Error('Mnemo: request failed')\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s)\n } catch {\n return s\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["export class MnemoError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MnemoError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class MnemoHTTPError extends MnemoError {\n constructor(\n message: string,\n readonly status: number,\n readonly body?: unknown,\n ) {\n super(`[${status}] ${message}`)\n this.name = 'MnemoHTTPError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class MnemoTimeoutError extends MnemoError {\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`)\n this.name = 'MnemoTimeoutError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n","/**\n * Mnemo Memory client.\n *\n * Zero runtime dependencies — uses the global `fetch` (Node 18+, Bun, browsers,\n * Cloudflare Workers, Deno, etc).\n *\n * @example\n * ```ts\n * import { Mnemo } from 'getmnemo'\n *\n * const memory = new Mnemo({\n * apiKey: process.env.GETMNEMO_API_KEY!,\n * workspaceId: process.env.GETMNEMO_WORKSPACE_ID!,\n * })\n *\n * await memory.add({ content: 'User prefers Japanese rice.', containerTag: 'user:jane' })\n * const { results } = await memory.search({ q: 'what rice does the user like?', containerTag: 'user:jane' })\n * ```\n */\n\nimport { MnemoHTTPError, MnemoTimeoutError } from './errors.js'\nimport type {\n AddResponse,\n ClientConfig,\n Memory,\n PaginatedMemories,\n Scope,\n SearchResponse,\n} from './types.js'\n\nconst DEFAULT_BASE_URL = 'https://api.mnemohq.com'\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst SDK_VERSION = '0.2.0'\nconst DEFAULT_SEARCH_LIMIT = 8\nconst USER_AGENT = `getmnemo/${SDK_VERSION}`\nconst DEFAULT_MAX_RETRIES = 3\nconst RETRY_BASE_DELAY_MS = 200\nconst RETRY_MAX_DELAY_MS = 5_000\n\n// Browsers reject `user-agent` as a forbidden header — setting it via fetch\n// throws or warns. Detect a browser-like environment so we can skip it there.\nconst IS_BROWSER_LIKE =\n typeof globalThis !== 'undefined' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (globalThis as any).window !== 'undefined' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (globalThis as any).document !== 'undefined'\n\nfunction retryDelayMs(attempt: number): number {\n const capped = Math.min(RETRY_BASE_DELAY_MS * 2 ** attempt, RETRY_MAX_DELAY_MS)\n // Full jitter.\n return Math.floor(Math.random() * capped)\n}\n\nfunction isRetryableStatus(status: number): boolean {\n // 501 Not Implemented is a permanent failure — retrying just wastes round-trips.\n if (status === 501) return false\n return status === 429 || (status >= 500 && status < 600)\n}\n\nfunction parseRetryAfterMs(headerValue: string | null): number | null {\n if (!headerValue) return null\n const trimmed = headerValue.trim()\n // Delta-seconds form.\n const seconds = Number(trimmed)\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(seconds * 1000, RETRY_MAX_DELAY_MS)\n }\n // HTTP-date form.\n const epoch = Date.parse(trimmed)\n if (!Number.isNaN(epoch)) {\n const delta = epoch - Date.now()\n return Math.max(0, Math.min(delta, RETRY_MAX_DELAY_MS))\n }\n return null\n}\n\nfunction delayForResponse(res: Response, attempt: number): number {\n const hint = parseRetryAfterMs(res.headers.get('retry-after'))\n return hint !== null ? hint : retryDelayMs(attempt)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport class Mnemo {\n readonly #baseUrl: string\n readonly #headers: Record<string, string>\n readonly #fetch: typeof fetch\n readonly #timeoutMs: number\n readonly #maxRetries: number\n readonly #defaultContainerTag: string | undefined\n\n constructor(cfg: ClientConfig) {\n if (!cfg.apiKey) throw new Error('Mnemo: apiKey is required')\n if (!cfg.workspaceId) throw new Error('Mnemo: workspaceId is required')\n this.#baseUrl = (cfg.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.#headers = {\n authorization: `Bearer ${cfg.apiKey}`,\n 'x-workspace-id': cfg.workspaceId,\n 'content-type': 'application/json',\n }\n // `user-agent` is on the forbidden header list in browsers — setting it\n // via fetch is silently dropped or throws. Send `x-getmnemo-client` as\n // an SDK identifier in browsers, and the standard User-Agent on Node.\n if (IS_BROWSER_LIKE) {\n this.#headers['x-getmnemo-client'] = USER_AGENT\n } else {\n this.#headers['user-agent'] = USER_AGENT\n }\n this.#defaultContainerTag = cfg.defaultContainerTag\n this.#fetch = cfg.fetch ?? fetch\n this.#timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS\n this.#maxRetries = Math.max(0, cfg.maxRetries ?? DEFAULT_MAX_RETRIES)\n }\n\n /**\n * Resolve the container for a call into the request fields the API expects.\n * A structured `scope` wins over a `containerTag` string; both fall back to\n * the constructor's `defaultContainerTag`. Throws if none is available.\n */\n #resolveContainer(\n method: 'add' | 'search',\n input: { containerTag?: string; scope?: Scope },\n ): { containerTag: string } | { scope: Scope } {\n if (input.scope) return { scope: input.scope }\n const tag = input.containerTag ?? this.#defaultContainerTag\n if (tag) return { containerTag: tag }\n throw new Error(\n `Mnemo.${method}: a container is required — pass containerTag (e.g. \"user:jane\") ` +\n 'or scope ({ type, id }) per call, or set defaultContainerTag on the client.',\n )\n }\n\n /**\n * Hybrid retrieval. Requires a container — pass `containerTag` (e.g.\n * `\"user:jane\"`) or `scope`, or set `defaultContainerTag` on the client.\n *\n * Sends `POST /v1/search` with body `{ q, limit, containerTag|scope }`.\n */\n async search(input: {\n q: string\n containerTag?: string\n scope?: Scope\n limit?: number\n searchMode?: string\n }): Promise<SearchResponse> {\n const container = this.#resolveContainer('search', input)\n return this.#request<SearchResponse>('POST', '/v1/search', {\n q: input.q,\n limit: input.limit ?? DEFAULT_SEARCH_LIMIT,\n ...(input.searchMode !== undefined ? { searchMode: input.searchMode } : {}),\n ...container,\n })\n }\n\n /**\n * Store an atomic fact. Requires a container — pass `containerTag` (e.g.\n * `\"user:jane\"`) or `scope`, or set `defaultContainerTag` on the client.\n *\n * Sends `POST /v1/memories` with body\n * `{ items: [{ content, metadata? }], containerTag|scope }`.\n */\n async add(input: {\n content: string\n containerTag?: string\n scope?: Scope\n metadata?: Record<string, unknown>\n }): Promise<AddResponse> {\n const container = this.#resolveContainer('add', input)\n return this.#request<AddResponse>('POST', '/v1/memories', {\n items: [\n {\n content: input.content,\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n },\n ],\n ...container,\n })\n }\n\n /**\n * Patch an existing memory by id.\n * Sends `PATCH /v1/memories/{memoryId}` with body `UpdateMemoryDto`\n * `{ content?, memoryType?, metadata?, source? }` (none required).\n */\n async update(\n memoryId: string,\n input: {\n content?: string\n memoryType?: string\n metadata?: Record<string, unknown>\n source?: string\n },\n ): Promise<Memory> {\n if (\n input.content === undefined &&\n input.memoryType === undefined &&\n input.metadata === undefined &&\n input.source === undefined\n ) {\n throw new Error(\n 'Mnemo.update: at least one of content/memoryType/metadata/source must be provided',\n )\n }\n return this.#request<Memory>(\n 'PATCH',\n `/v1/memories/${encodeURIComponent(memoryId)}`,\n input,\n )\n }\n\n /** Fetch a single memory by id. Sends `GET /v1/memories/{memoryId}`. */\n async get(memoryId: string): Promise<Memory> {\n return this.#request<Memory>('GET', `/v1/memories/${encodeURIComponent(memoryId)}`)\n }\n\n /** Remove a memory by id. Sends `DELETE /v1/memories/{memoryId}`. */\n async delete(memoryId: string): Promise<void> {\n await this.#request<unknown>(\n 'DELETE',\n `/v1/memories/${encodeURIComponent(memoryId)}`,\n )\n }\n\n /**\n * Cursor-paginated list of memories, optionally filtered by container.\n * Sends `GET /v1/memories` with query\n * `limit?, cursor?, scopeType?, scopeId?, containerTag?`.\n */\n async list(input?: {\n containerTag?: string\n limit?: number\n cursor?: string\n scopeType?: string\n scopeId?: string\n }): Promise<PaginatedMemories> {\n const params = new URLSearchParams()\n if (input?.limit !== undefined) params.set('limit', String(input.limit))\n if (input?.cursor !== undefined) params.set('cursor', input.cursor)\n if (input?.scopeType !== undefined) params.set('scopeType', input.scopeType)\n if (input?.scopeId !== undefined) params.set('scopeId', input.scopeId)\n if (input?.containerTag !== undefined) params.set('containerTag', input.containerTag)\n const qs = params.toString()\n return this.#request<PaginatedMemories>('GET', `/v1/memories${qs ? `?${qs}` : ''}`)\n }\n\n /** Echoed back for debugging — never sent to the wire. */\n get defaultContainerTag(): string | undefined {\n return this.#defaultContainerTag\n }\n\n async #request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const serializedBody = body === undefined ? undefined : JSON.stringify(body)\n let lastErr: unknown\n for (let attempt = 0; attempt <= this.#maxRetries; attempt++) {\n const ctrl = new AbortController()\n const timer = setTimeout(() => ctrl.abort(), this.#timeoutMs)\n try {\n const res = await this.#fetch(`${this.#baseUrl}${path}`, {\n method,\n headers: { ...this.#headers },\n body: serializedBody,\n signal: ctrl.signal,\n })\n if (isRetryableStatus(res.status) && attempt < this.#maxRetries) {\n // Capture Retry-After before draining; some runtimes invalidate\n // headers once the body is consumed.\n const wait = delayForResponse(res, attempt)\n // Drain body so the underlying connection can be reused.\n await res.text().catch(() => undefined)\n await sleep(wait)\n continue\n }\n const text = await res.text()\n const parsed: unknown = text ? safeJson(text) : undefined\n if (!res.ok) {\n const message =\n (parsed && typeof parsed === 'object' && 'message' in parsed\n ? String((parsed as { message: unknown }).message)\n : null) ?? `HTTP ${res.status} ${res.statusText}`\n throw new MnemoHTTPError(message, res.status, parsed)\n }\n return parsed as T\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new MnemoTimeoutError(this.#timeoutMs)\n }\n if (err instanceof MnemoHTTPError) throw err\n lastErr = err\n if (attempt < this.#maxRetries) {\n await sleep(retryDelayMs(attempt))\n continue\n }\n throw err\n } finally {\n clearTimeout(timer)\n }\n }\n throw lastErr ?? new Error('Mnemo: request failed')\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s)\n } catch {\n return s\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,46 +1,116 @@
1
1
  /**
2
2
  * Public types for the Mnemo SDK.
3
3
  *
4
- * These mirror the REST API response shapes one-to-one. We deliberately keep
5
- * them as plain interfaces (no zod runtime cost) — runtime validation is the
6
- * caller's responsibility if they need it.
4
+ * Request types are derived from the OpenAPI spec at
5
+ * `https://api.mnemohq.com/openapi.json` ("Mnemo API" v0.2.0).
6
+ *
7
+ * Response types are confirmed against real prod payloads captured 2026-06-16
8
+ * (the spec does NOT annotate response schemas — every memories/search op
9
+ * returns `{}` — so these shapes were finalized from live responses).
10
+ */
11
+ /**
12
+ * Structured container scope. The alternative to the `containerTag` string —
13
+ * e.g. `{ type: 'user', id: 'jane' }` is equivalent to `containerTag: 'user:jane'`.
14
+ */
15
+ type Scope = {
16
+ type: string;
17
+ id: string;
18
+ };
19
+ /** The container a memory belongs to. */
20
+ type Container = {
21
+ id: string;
22
+ tag: string;
23
+ containerType: string;
24
+ displayName: string;
25
+ };
26
+ /**
27
+ * A single memory as returned by `get`/`update`, and the per-item shape inside
28
+ * `list` and `add` responses.
7
29
  */
8
30
  type Memory = {
9
31
  id: string;
32
+ scope: Scope;
33
+ scopeKey: string;
34
+ container: Container;
10
35
  content: string;
11
- metadata?: Record<string, unknown>;
12
- workspaceId: string;
13
- actorId?: string | null;
36
+ contentHash: string;
37
+ idempotencyKey: string | null;
38
+ memoryType: string;
39
+ metadata: Record<string, unknown> | null;
40
+ source: unknown | null;
41
+ sourceDocumentId: string | null;
42
+ eventId: string | null;
43
+ deletedAt: string | null;
14
44
  createdAt: string;
15
45
  updatedAt: string;
16
46
  };
17
- type SearchSource = {
18
- documentId?: string;
19
- chunkId?: string;
47
+ /**
48
+ * One stored item in an `add` response. Same fields as a `Memory`.
49
+ */
50
+ type AddedItem = Memory;
51
+ /** Response shape of `add()`. */
52
+ type AddResponse = {
53
+ scopeKey: string;
54
+ scope: Scope;
55
+ items: AddedItem[];
20
56
  };
57
+ /** One retrieval hit in a `search` response. */
21
58
  type SearchHit = {
59
+ resultType: string;
22
60
  memoryId: string;
61
+ scopeKey: string;
23
62
  content: string;
63
+ metadata: Record<string, unknown> | null;
64
+ memoryType: string;
65
+ polarity: string;
24
66
  score: number;
25
- metadata?: Record<string, unknown>;
26
- source?: SearchSource | null;
67
+ createdAt: string;
68
+ updatedAt: string;
27
69
  };
70
+ /** Per-stage timing breakdown returned by `search`. */
71
+ type SearchLatency = {
72
+ parallelMs: number;
73
+ strategyMs: number;
74
+ fusionMs: number;
75
+ rerankerMs: number;
76
+ totalMs: number;
77
+ };
78
+ /**
79
+ * Response shape of `search()`. The primary results live in `results`; the
80
+ * `positivePreferences` and `hardConstraints` arrays carry the same `SearchHit`
81
+ * shape, alongside retrieval metadata.
82
+ */
28
83
  type SearchResponse = {
29
- hits: SearchHit[];
30
- query: string;
31
- latencyMs: number;
84
+ results: SearchHit[];
85
+ positivePreferences: SearchHit[];
86
+ hardConstraints: SearchHit[];
87
+ searchMode: string;
88
+ queryIntent: string;
89
+ queryIntentConfidence: number;
90
+ abstained: boolean;
91
+ reranked: boolean;
92
+ rawBestVectorSim: number;
93
+ latency: SearchLatency;
32
94
  };
95
+ /** Cursor-paginated list of memories. */
33
96
  type PaginatedMemories = {
34
97
  items: Memory[];
35
98
  nextCursor: string | null;
36
99
  };
37
100
  type ClientConfig = {
38
- /** Required. Get one at https://app.mnemohq.com/settings/api-keys. */
101
+ /**
102
+ * Required. Full-access by default — keep server-side. For client-exposed
103
+ * contexts, mint a scoped read-only key. Get one at
104
+ * https://app.mnemohq.com/settings/api-keys.
105
+ */
39
106
  apiKey: string;
40
- /** Required. Workspace ID from the dashboard URL. */
107
+ /** Required. Workspace ID sent as the `x-workspace-id` header on every call. */
41
108
  workspaceId: string;
42
- /** Optional default actor scope for all calls (overridable per-method). */
43
- actorId?: string;
109
+ /**
110
+ * Optional default container tag (e.g. `"user:jane"`). When set, `add` and
111
+ * `search` fall back to it if no per-call `containerTag`/`scope` is given.
112
+ */
113
+ defaultContainerTag?: string;
44
114
  /** Defaults to https://api.mnemohq.com. */
45
115
  baseUrl?: string;
46
116
  /** Per-request timeout in ms (default 30s). */
@@ -59,44 +129,76 @@ type ClientConfig = {
59
129
  *
60
130
  * @example
61
131
  * ```ts
62
- * import { Mnemo } from '@mnemo/memory'
132
+ * import { Mnemo } from 'getmnemo'
63
133
  *
64
134
  * const memory = new Mnemo({
65
135
  * apiKey: process.env.GETMNEMO_API_KEY!,
66
136
  * workspaceId: process.env.GETMNEMO_WORKSPACE_ID!,
67
137
  * })
68
138
  *
69
- * await memory.add({ content: 'User prefers Japanese rice.' })
70
- * const { hits } = await memory.search({ query: 'what rice does the user like?' })
139
+ * await memory.add({ content: 'User prefers Japanese rice.', containerTag: 'user:jane' })
140
+ * const { results } = await memory.search({ q: 'what rice does the user like?', containerTag: 'user:jane' })
71
141
  * ```
72
142
  */
73
143
 
74
144
  declare class Mnemo {
75
145
  #private;
76
146
  constructor(cfg: ClientConfig);
147
+ /**
148
+ * Hybrid retrieval. Requires a container — pass `containerTag` (e.g.
149
+ * `"user:jane"`) or `scope`, or set `defaultContainerTag` on the client.
150
+ *
151
+ * Sends `POST /v1/search` with body `{ q, limit, containerTag|scope }`.
152
+ */
77
153
  search(input: {
78
- query: string;
154
+ q: string;
155
+ containerTag?: string;
156
+ scope?: Scope;
79
157
  limit?: number;
80
- actorId?: string;
158
+ searchMode?: string;
81
159
  }): Promise<SearchResponse>;
160
+ /**
161
+ * Store an atomic fact. Requires a container — pass `containerTag` (e.g.
162
+ * `"user:jane"`) or `scope`, or set `defaultContainerTag` on the client.
163
+ *
164
+ * Sends `POST /v1/memories` with body
165
+ * `{ items: [{ content, metadata? }], containerTag|scope }`.
166
+ */
82
167
  add(input: {
83
168
  content: string;
169
+ containerTag?: string;
170
+ scope?: Scope;
84
171
  metadata?: Record<string, unknown>;
85
- actorId?: string;
86
- }): Promise<Memory>;
87
- update(id: string, input: {
172
+ }): Promise<AddResponse>;
173
+ /**
174
+ * Patch an existing memory by id.
175
+ * Sends `PATCH /v1/memories/{memoryId}` with body `UpdateMemoryDto`
176
+ * `{ content?, memoryType?, metadata?, source? }` (none required).
177
+ */
178
+ update(memoryId: string, input: {
88
179
  content?: string;
180
+ memoryType?: string;
89
181
  metadata?: Record<string, unknown>;
182
+ source?: string;
90
183
  }): Promise<Memory>;
91
- get(id: string): Promise<Memory>;
92
- delete(id: string): Promise<void>;
184
+ /** Fetch a single memory by id. Sends `GET /v1/memories/{memoryId}`. */
185
+ get(memoryId: string): Promise<Memory>;
186
+ /** Remove a memory by id. Sends `DELETE /v1/memories/{memoryId}`. */
187
+ delete(memoryId: string): Promise<void>;
188
+ /**
189
+ * Cursor-paginated list of memories, optionally filtered by container.
190
+ * Sends `GET /v1/memories` with query
191
+ * `limit?, cursor?, scopeType?, scopeId?, containerTag?`.
192
+ */
93
193
  list(input?: {
194
+ containerTag?: string;
94
195
  limit?: number;
95
196
  cursor?: string;
96
- actorId?: string;
197
+ scopeType?: string;
198
+ scopeId?: string;
97
199
  }): Promise<PaginatedMemories>;
98
200
  /** Echoed back for debugging — never sent to the wire. */
99
- get defaultActorId(): string | undefined;
201
+ get defaultContainerTag(): string | undefined;
100
202
  }
101
203
 
102
204
  declare class MnemoError extends Error {
@@ -111,4 +213,4 @@ declare class MnemoTimeoutError extends MnemoError {
111
213
  constructor(timeoutMs: number);
112
214
  }
113
215
 
114
- export { type ClientConfig, type Memory, Mnemo, MnemoError, MnemoHTTPError, MnemoTimeoutError, type PaginatedMemories, type SearchHit, type SearchResponse, type SearchSource };
216
+ export { type AddResponse, type AddedItem, type ClientConfig, type Container, type Memory, Mnemo, MnemoError, MnemoHTTPError, MnemoTimeoutError, type PaginatedMemories, type Scope, type SearchHit, type SearchResponse };
package/dist/index.d.ts CHANGED
@@ -1,46 +1,116 @@
1
1
  /**
2
2
  * Public types for the Mnemo SDK.
3
3
  *
4
- * These mirror the REST API response shapes one-to-one. We deliberately keep
5
- * them as plain interfaces (no zod runtime cost) — runtime validation is the
6
- * caller's responsibility if they need it.
4
+ * Request types are derived from the OpenAPI spec at
5
+ * `https://api.mnemohq.com/openapi.json` ("Mnemo API" v0.2.0).
6
+ *
7
+ * Response types are confirmed against real prod payloads captured 2026-06-16
8
+ * (the spec does NOT annotate response schemas — every memories/search op
9
+ * returns `{}` — so these shapes were finalized from live responses).
10
+ */
11
+ /**
12
+ * Structured container scope. The alternative to the `containerTag` string —
13
+ * e.g. `{ type: 'user', id: 'jane' }` is equivalent to `containerTag: 'user:jane'`.
14
+ */
15
+ type Scope = {
16
+ type: string;
17
+ id: string;
18
+ };
19
+ /** The container a memory belongs to. */
20
+ type Container = {
21
+ id: string;
22
+ tag: string;
23
+ containerType: string;
24
+ displayName: string;
25
+ };
26
+ /**
27
+ * A single memory as returned by `get`/`update`, and the per-item shape inside
28
+ * `list` and `add` responses.
7
29
  */
8
30
  type Memory = {
9
31
  id: string;
32
+ scope: Scope;
33
+ scopeKey: string;
34
+ container: Container;
10
35
  content: string;
11
- metadata?: Record<string, unknown>;
12
- workspaceId: string;
13
- actorId?: string | null;
36
+ contentHash: string;
37
+ idempotencyKey: string | null;
38
+ memoryType: string;
39
+ metadata: Record<string, unknown> | null;
40
+ source: unknown | null;
41
+ sourceDocumentId: string | null;
42
+ eventId: string | null;
43
+ deletedAt: string | null;
14
44
  createdAt: string;
15
45
  updatedAt: string;
16
46
  };
17
- type SearchSource = {
18
- documentId?: string;
19
- chunkId?: string;
47
+ /**
48
+ * One stored item in an `add` response. Same fields as a `Memory`.
49
+ */
50
+ type AddedItem = Memory;
51
+ /** Response shape of `add()`. */
52
+ type AddResponse = {
53
+ scopeKey: string;
54
+ scope: Scope;
55
+ items: AddedItem[];
20
56
  };
57
+ /** One retrieval hit in a `search` response. */
21
58
  type SearchHit = {
59
+ resultType: string;
22
60
  memoryId: string;
61
+ scopeKey: string;
23
62
  content: string;
63
+ metadata: Record<string, unknown> | null;
64
+ memoryType: string;
65
+ polarity: string;
24
66
  score: number;
25
- metadata?: Record<string, unknown>;
26
- source?: SearchSource | null;
67
+ createdAt: string;
68
+ updatedAt: string;
27
69
  };
70
+ /** Per-stage timing breakdown returned by `search`. */
71
+ type SearchLatency = {
72
+ parallelMs: number;
73
+ strategyMs: number;
74
+ fusionMs: number;
75
+ rerankerMs: number;
76
+ totalMs: number;
77
+ };
78
+ /**
79
+ * Response shape of `search()`. The primary results live in `results`; the
80
+ * `positivePreferences` and `hardConstraints` arrays carry the same `SearchHit`
81
+ * shape, alongside retrieval metadata.
82
+ */
28
83
  type SearchResponse = {
29
- hits: SearchHit[];
30
- query: string;
31
- latencyMs: number;
84
+ results: SearchHit[];
85
+ positivePreferences: SearchHit[];
86
+ hardConstraints: SearchHit[];
87
+ searchMode: string;
88
+ queryIntent: string;
89
+ queryIntentConfidence: number;
90
+ abstained: boolean;
91
+ reranked: boolean;
92
+ rawBestVectorSim: number;
93
+ latency: SearchLatency;
32
94
  };
95
+ /** Cursor-paginated list of memories. */
33
96
  type PaginatedMemories = {
34
97
  items: Memory[];
35
98
  nextCursor: string | null;
36
99
  };
37
100
  type ClientConfig = {
38
- /** Required. Get one at https://app.mnemohq.com/settings/api-keys. */
101
+ /**
102
+ * Required. Full-access by default — keep server-side. For client-exposed
103
+ * contexts, mint a scoped read-only key. Get one at
104
+ * https://app.mnemohq.com/settings/api-keys.
105
+ */
39
106
  apiKey: string;
40
- /** Required. Workspace ID from the dashboard URL. */
107
+ /** Required. Workspace ID sent as the `x-workspace-id` header on every call. */
41
108
  workspaceId: string;
42
- /** Optional default actor scope for all calls (overridable per-method). */
43
- actorId?: string;
109
+ /**
110
+ * Optional default container tag (e.g. `"user:jane"`). When set, `add` and
111
+ * `search` fall back to it if no per-call `containerTag`/`scope` is given.
112
+ */
113
+ defaultContainerTag?: string;
44
114
  /** Defaults to https://api.mnemohq.com. */
45
115
  baseUrl?: string;
46
116
  /** Per-request timeout in ms (default 30s). */
@@ -59,44 +129,76 @@ type ClientConfig = {
59
129
  *
60
130
  * @example
61
131
  * ```ts
62
- * import { Mnemo } from '@mnemo/memory'
132
+ * import { Mnemo } from 'getmnemo'
63
133
  *
64
134
  * const memory = new Mnemo({
65
135
  * apiKey: process.env.GETMNEMO_API_KEY!,
66
136
  * workspaceId: process.env.GETMNEMO_WORKSPACE_ID!,
67
137
  * })
68
138
  *
69
- * await memory.add({ content: 'User prefers Japanese rice.' })
70
- * const { hits } = await memory.search({ query: 'what rice does the user like?' })
139
+ * await memory.add({ content: 'User prefers Japanese rice.', containerTag: 'user:jane' })
140
+ * const { results } = await memory.search({ q: 'what rice does the user like?', containerTag: 'user:jane' })
71
141
  * ```
72
142
  */
73
143
 
74
144
  declare class Mnemo {
75
145
  #private;
76
146
  constructor(cfg: ClientConfig);
147
+ /**
148
+ * Hybrid retrieval. Requires a container — pass `containerTag` (e.g.
149
+ * `"user:jane"`) or `scope`, or set `defaultContainerTag` on the client.
150
+ *
151
+ * Sends `POST /v1/search` with body `{ q, limit, containerTag|scope }`.
152
+ */
77
153
  search(input: {
78
- query: string;
154
+ q: string;
155
+ containerTag?: string;
156
+ scope?: Scope;
79
157
  limit?: number;
80
- actorId?: string;
158
+ searchMode?: string;
81
159
  }): Promise<SearchResponse>;
160
+ /**
161
+ * Store an atomic fact. Requires a container — pass `containerTag` (e.g.
162
+ * `"user:jane"`) or `scope`, or set `defaultContainerTag` on the client.
163
+ *
164
+ * Sends `POST /v1/memories` with body
165
+ * `{ items: [{ content, metadata? }], containerTag|scope }`.
166
+ */
82
167
  add(input: {
83
168
  content: string;
169
+ containerTag?: string;
170
+ scope?: Scope;
84
171
  metadata?: Record<string, unknown>;
85
- actorId?: string;
86
- }): Promise<Memory>;
87
- update(id: string, input: {
172
+ }): Promise<AddResponse>;
173
+ /**
174
+ * Patch an existing memory by id.
175
+ * Sends `PATCH /v1/memories/{memoryId}` with body `UpdateMemoryDto`
176
+ * `{ content?, memoryType?, metadata?, source? }` (none required).
177
+ */
178
+ update(memoryId: string, input: {
88
179
  content?: string;
180
+ memoryType?: string;
89
181
  metadata?: Record<string, unknown>;
182
+ source?: string;
90
183
  }): Promise<Memory>;
91
- get(id: string): Promise<Memory>;
92
- delete(id: string): Promise<void>;
184
+ /** Fetch a single memory by id. Sends `GET /v1/memories/{memoryId}`. */
185
+ get(memoryId: string): Promise<Memory>;
186
+ /** Remove a memory by id. Sends `DELETE /v1/memories/{memoryId}`. */
187
+ delete(memoryId: string): Promise<void>;
188
+ /**
189
+ * Cursor-paginated list of memories, optionally filtered by container.
190
+ * Sends `GET /v1/memories` with query
191
+ * `limit?, cursor?, scopeType?, scopeId?, containerTag?`.
192
+ */
93
193
  list(input?: {
194
+ containerTag?: string;
94
195
  limit?: number;
95
196
  cursor?: string;
96
- actorId?: string;
197
+ scopeType?: string;
198
+ scopeId?: string;
97
199
  }): Promise<PaginatedMemories>;
98
200
  /** Echoed back for debugging — never sent to the wire. */
99
- get defaultActorId(): string | undefined;
201
+ get defaultContainerTag(): string | undefined;
100
202
  }
101
203
 
102
204
  declare class MnemoError extends Error {
@@ -111,4 +213,4 @@ declare class MnemoTimeoutError extends MnemoError {
111
213
  constructor(timeoutMs: number);
112
214
  }
113
215
 
114
- export { type ClientConfig, type Memory, Mnemo, MnemoError, MnemoHTTPError, MnemoTimeoutError, type PaginatedMemories, type SearchHit, type SearchResponse, type SearchSource };
216
+ export { type AddResponse, type AddedItem, type ClientConfig, type Container, type Memory, Mnemo, MnemoError, MnemoHTTPError, MnemoTimeoutError, type PaginatedMemories, type Scope, type SearchHit, type SearchResponse };
package/dist/index.js CHANGED
@@ -28,8 +28,9 @@ var MnemoTimeoutError = class extends MnemoError {
28
28
  // src/client.ts
29
29
  var DEFAULT_BASE_URL = "https://api.mnemohq.com";
30
30
  var DEFAULT_TIMEOUT_MS = 3e4;
31
- var SDK_VERSION = "0.1.0";
32
- var USER_AGENT = `@mnemo/memory/${SDK_VERSION}`;
31
+ var SDK_VERSION = "0.2.0";
32
+ var DEFAULT_SEARCH_LIMIT = 8;
33
+ var USER_AGENT = `getmnemo/${SDK_VERSION}`;
33
34
  var DEFAULT_MAX_RETRIES = 3;
34
35
  var RETRY_BASE_DELAY_MS = 200;
35
36
  var RETRY_MAX_DELAY_MS = 5e3;
@@ -71,7 +72,7 @@ var Mnemo = class {
71
72
  #fetch;
72
73
  #timeoutMs;
73
74
  #maxRetries;
74
- #defaultActorId;
75
+ #defaultContainerTag;
75
76
  constructor(cfg) {
76
77
  if (!cfg.apiKey) throw new Error("Mnemo: apiKey is required");
77
78
  if (!cfg.workspaceId) throw new Error("Mnemo: workspaceId is required");
@@ -86,49 +87,104 @@ var Mnemo = class {
86
87
  } else {
87
88
  this.#headers["user-agent"] = USER_AGENT;
88
89
  }
89
- if (cfg.actorId) this.#headers["x-actor-id"] = cfg.actorId;
90
- this.#defaultActorId = cfg.actorId;
90
+ this.#defaultContainerTag = cfg.defaultContainerTag;
91
91
  this.#fetch = cfg.fetch ?? fetch;
92
92
  this.#timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS;
93
93
  this.#maxRetries = Math.max(0, cfg.maxRetries ?? DEFAULT_MAX_RETRIES);
94
94
  }
95
+ /**
96
+ * Resolve the container for a call into the request fields the API expects.
97
+ * A structured `scope` wins over a `containerTag` string; both fall back to
98
+ * the constructor's `defaultContainerTag`. Throws if none is available.
99
+ */
100
+ #resolveContainer(method, input) {
101
+ if (input.scope) return { scope: input.scope };
102
+ const tag = input.containerTag ?? this.#defaultContainerTag;
103
+ if (tag) return { containerTag: tag };
104
+ throw new Error(
105
+ `Mnemo.${method}: a container is required \u2014 pass containerTag (e.g. "user:jane") or scope ({ type, id }) per call, or set defaultContainerTag on the client.`
106
+ );
107
+ }
108
+ /**
109
+ * Hybrid retrieval. Requires a container — pass `containerTag` (e.g.
110
+ * `"user:jane"`) or `scope`, or set `defaultContainerTag` on the client.
111
+ *
112
+ * Sends `POST /v1/search` with body `{ q, limit, containerTag|scope }`.
113
+ */
95
114
  async search(input) {
115
+ const container = this.#resolveContainer("search", input);
96
116
  return this.#request("POST", "/v1/search", {
97
- query: input.query,
98
- limit: input.limit ?? 8,
99
- ...input.actorId !== void 0 ? { actorId: input.actorId } : {}
117
+ q: input.q,
118
+ limit: input.limit ?? DEFAULT_SEARCH_LIMIT,
119
+ ...input.searchMode !== void 0 ? { searchMode: input.searchMode } : {},
120
+ ...container
100
121
  });
101
122
  }
123
+ /**
124
+ * Store an atomic fact. Requires a container — pass `containerTag` (e.g.
125
+ * `"user:jane"`) or `scope`, or set `defaultContainerTag` on the client.
126
+ *
127
+ * Sends `POST /v1/memories` with body
128
+ * `{ items: [{ content, metadata? }], containerTag|scope }`.
129
+ */
102
130
  async add(input) {
131
+ const container = this.#resolveContainer("add", input);
103
132
  return this.#request("POST", "/v1/memories", {
104
- content: input.content,
105
- ...input.metadata !== void 0 ? { metadata: input.metadata } : {},
106
- ...input.actorId !== void 0 ? { actorId: input.actorId } : {}
133
+ items: [
134
+ {
135
+ content: input.content,
136
+ ...input.metadata !== void 0 ? { metadata: input.metadata } : {}
137
+ }
138
+ ],
139
+ ...container
107
140
  });
108
141
  }
109
- async update(id, input) {
110
- if (input.content === void 0 && input.metadata === void 0) {
111
- throw new Error("Mnemo.update: at least one of content/metadata must be provided");
142
+ /**
143
+ * Patch an existing memory by id.
144
+ * Sends `PATCH /v1/memories/{memoryId}` with body `UpdateMemoryDto`
145
+ * `{ content?, memoryType?, metadata?, source? }` (none required).
146
+ */
147
+ async update(memoryId, input) {
148
+ if (input.content === void 0 && input.memoryType === void 0 && input.metadata === void 0 && input.source === void 0) {
149
+ throw new Error(
150
+ "Mnemo.update: at least one of content/memoryType/metadata/source must be provided"
151
+ );
112
152
  }
113
- return this.#request("PATCH", `/v1/memories/${encodeURIComponent(id)}`, input);
153
+ return this.#request(
154
+ "PATCH",
155
+ `/v1/memories/${encodeURIComponent(memoryId)}`,
156
+ input
157
+ );
114
158
  }
115
- async get(id) {
116
- return this.#request("GET", `/v1/memories/${encodeURIComponent(id)}`);
159
+ /** Fetch a single memory by id. Sends `GET /v1/memories/{memoryId}`. */
160
+ async get(memoryId) {
161
+ return this.#request("GET", `/v1/memories/${encodeURIComponent(memoryId)}`);
117
162
  }
118
- async delete(id) {
119
- await this.#request("DELETE", `/v1/memories/${encodeURIComponent(id)}`);
163
+ /** Remove a memory by id. Sends `DELETE /v1/memories/{memoryId}`. */
164
+ async delete(memoryId) {
165
+ await this.#request(
166
+ "DELETE",
167
+ `/v1/memories/${encodeURIComponent(memoryId)}`
168
+ );
120
169
  }
170
+ /**
171
+ * Cursor-paginated list of memories, optionally filtered by container.
172
+ * Sends `GET /v1/memories` with query
173
+ * `limit?, cursor?, scopeType?, scopeId?, containerTag?`.
174
+ */
121
175
  async list(input) {
122
176
  const params = new URLSearchParams();
123
177
  if (input?.limit !== void 0) params.set("limit", String(input.limit));
124
178
  if (input?.cursor !== void 0) params.set("cursor", input.cursor);
125
- if (input?.actorId !== void 0) params.set("actorId", input.actorId);
179
+ if (input?.scopeType !== void 0) params.set("scopeType", input.scopeType);
180
+ if (input?.scopeId !== void 0) params.set("scopeId", input.scopeId);
181
+ if (input?.containerTag !== void 0) params.set("containerTag", input.containerTag);
126
182
  const qs = params.toString();
127
183
  return this.#request("GET", `/v1/memories${qs ? `?${qs}` : ""}`);
128
184
  }
129
185
  /** Echoed back for debugging — never sent to the wire. */
130
- get defaultActorId() {
131
- return this.#defaultActorId;
186
+ get defaultContainerTag() {
187
+ return this.#defaultContainerTag;
132
188
  }
133
189
  async #request(method, path, body) {
134
190
  const serializedBody = body === void 0 ? void 0 : JSON.stringify(body);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":";AAAO,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EACpC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF;AAEO,IAAM,cAAA,GAAN,cAA6B,UAAA,CAAW;AAAA,EAC7C,WAAA,CACE,OAAA,EACS,MAAA,EACA,IAAA,EACT;AACA,IAAA,KAAA,CAAM,CAAA,CAAA,EAAI,MAAM,CAAA,EAAA,EAAK,OAAO,CAAA,CAAE,CAAA;AAHrB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAGT,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AAAA,EANW,MAAA;AAAA,EACA,IAAA;AAMb;AAEO,IAAM,iBAAA,GAAN,cAAgC,UAAA,CAAW;AAAA,EAChD,YAAY,SAAA,EAAmB;AAC7B,IAAA,KAAA,CAAM,CAAA,wBAAA,EAA2B,SAAS,CAAA,EAAA,CAAI,CAAA;AAC9C,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF;;;ACEA,IAAM,gBAAA,GAAmB,yBAAA;AACzB,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,WAAA,GAAc,OAAA;AACpB,IAAM,UAAA,GAAa,iBAAiB,WAAW,CAAA,CAAA;AAC/C,IAAM,mBAAA,GAAsB,CAAA;AAC5B,IAAM,mBAAA,GAAsB,GAAA;AAC5B,IAAM,kBAAA,GAAqB,GAAA;AAI3B,IAAM,eAAA,GACJ,OAAO,UAAA,KAAe,WAAA;AAEtB,OAAQ,WAAmB,MAAA,KAAW,WAAA;AAEtC,OAAQ,WAAmB,QAAA,KAAa,WAAA;AAE1C,SAAS,aAAa,OAAA,EAAyB;AAC7C,EAAA,MAAM,SAAS,IAAA,CAAK,GAAA,CAAI,mBAAA,GAAsB,CAAA,IAAK,SAAS,kBAAkB,CAAA;AAE9E,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,MAAM,CAAA;AAC1C;AAEA,SAAS,kBAAkB,MAAA,EAAyB;AAElD,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,KAAA;AAC3B,EAAA,OAAO,MAAA,KAAW,GAAA,IAAQ,MAAA,IAAU,GAAA,IAAO,MAAA,GAAS,GAAA;AACtD;AAEA,SAAS,kBAAkB,WAAA,EAA2C;AACpE,EAAA,IAAI,CAAC,aAAa,OAAO,IAAA;AACzB,EAAA,MAAM,OAAA,GAAU,YAAY,IAAA,EAAK;AAEjC,EAAA,MAAM,OAAA,GAAU,OAAO,OAAO,CAAA;AAC9B,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,IAAK,WAAW,CAAA,EAAG;AAC5C,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,OAAA,GAAU,GAAA,EAAM,kBAAkB,CAAA;AAAA,EACpD;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAChC,EAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,KAAK,CAAA,EAAG;AACxB,IAAA,MAAM,KAAA,GAAQ,KAAA,GAAQ,IAAA,CAAK,GAAA,EAAI;AAC/B,IAAA,OAAO,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,KAAA,EAAO,kBAAkB,CAAC,CAAA;AAAA,EACxD;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,gBAAA,CAAiB,KAAe,OAAA,EAAyB;AAChE,EAAA,MAAM,OAAO,iBAAA,CAAkB,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAC,CAAA;AAC7D,EAAA,OAAO,IAAA,KAAS,IAAA,GAAO,IAAA,GAAO,YAAA,CAAa,OAAO,CAAA;AACpD;AAEA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;AAEO,IAAM,QAAN,MAAY;AAAA,EACR,QAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,eAAA;AAAA,EAET,YAAY,GAAA,EAAmB;AAC7B,IAAA,IAAI,CAAC,GAAA,CAAI,MAAA,EAAQ,MAAM,IAAI,MAAM,2BAA2B,CAAA;AAC5D,IAAA,IAAI,CAAC,GAAA,CAAI,WAAA,EAAa,MAAM,IAAI,MAAM,gCAAgC,CAAA;AACtE,IAAA,IAAA,CAAK,YAAY,GAAA,CAAI,OAAA,IAAW,gBAAA,EAAkB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACnE,IAAA,IAAA,CAAK,QAAA,GAAW;AAAA,MACd,aAAA,EAAe,CAAA,OAAA,EAAU,GAAA,CAAI,MAAM,CAAA,CAAA;AAAA,MACnC,kBAAkB,GAAA,CAAI,WAAA;AAAA,MACtB,cAAA,EAAgB;AAAA,KAClB;AAIA,IAAA,IAAI,eAAA,EAAiB;AACnB,MAAA,IAAA,CAAK,QAAA,CAAS,mBAAmB,CAAA,GAAI,UAAA;AAAA,IACvC,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,QAAA,CAAS,YAAY,CAAA,GAAI,UAAA;AAAA,IAChC;AACA,IAAA,IAAI,IAAI,OAAA,EAAS,IAAA,CAAK,QAAA,CAAS,YAAY,IAAI,GAAA,CAAI,OAAA;AACnD,IAAA,IAAA,CAAK,kBAAkB,GAAA,CAAI,OAAA;AAC3B,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,KAAA,IAAS,KAAA;AAC3B,IAAA,IAAA,CAAK,UAAA,GAAa,IAAI,SAAA,IAAa,kBAAA;AACnC,IAAA,IAAA,CAAK,cAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAA,CAAI,cAAc,mBAAmB,CAAA;AAAA,EACtE;AAAA,EAEA,MAAM,OAAO,KAAA,EAIe;AAC1B,IAAA,OAAO,IAAA,CAAK,QAAA,CAAyB,MAAA,EAAQ,YAAA,EAAc;AAAA,MACzD,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,KAAA,EAAO,MAAM,KAAA,IAAS,CAAA;AAAA,MACtB,GAAI,MAAM,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,EAAS,KAAA,CAAM,OAAA,EAAQ,GAAI;AAAC,KACjE,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,KAAA,EAIU;AAClB,IAAA,OAAO,IAAA,CAAK,QAAA,CAAiB,MAAA,EAAQ,cAAA,EAAgB;AAAA,MACnD,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,GAAI,MAAM,QAAA,KAAa,MAAA,GAAY,EAAE,QAAA,EAAU,KAAA,CAAM,QAAA,EAAS,GAAI,EAAC;AAAA,MACnE,GAAI,MAAM,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,EAAS,KAAA,CAAM,OAAA,EAAQ,GAAI;AAAC,KACjE,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,MAAA,CACJ,EAAA,EACA,KAAA,EACiB;AACjB,IAAA,IAAI,KAAA,CAAM,OAAA,KAAY,MAAA,IAAa,KAAA,CAAM,aAAa,MAAA,EAAW;AAC/D,MAAA,MAAM,IAAI,MAAM,iEAAiE,CAAA;AAAA,IACnF;AACA,IAAA,OAAO,IAAA,CAAK,SAAiB,OAAA,EAAS,CAAA,aAAA,EAAgB,mBAAmB,EAAE,CAAC,IAAI,KAAK,CAAA;AAAA,EACvF;AAAA,EAEA,MAAM,IAAI,EAAA,EAA6B;AACrC,IAAA,OAAO,KAAK,QAAA,CAAiB,KAAA,EAAO,gBAAgB,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAE,CAAA;AAAA,EAC9E;AAAA,EAEA,MAAM,OAAO,EAAA,EAA2B;AACtC,IAAA,MAAM,KAAK,QAAA,CAAkB,QAAA,EAAU,gBAAgB,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAE,CAAA;AAAA,EACjF;AAAA,EAEA,MAAM,KAAK,KAAA,EAIoB;AAC7B,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,IAAA,IAAI,KAAA,EAAO,UAAU,MAAA,EAAW,MAAA,CAAO,IAAI,OAAA,EAAS,MAAA,CAAO,KAAA,CAAM,KAAK,CAAC,CAAA;AACvE,IAAA,IAAI,OAAO,MAAA,KAAW,MAAA,SAAkB,GAAA,CAAI,QAAA,EAAU,MAAM,MAAM,CAAA;AAClE,IAAA,IAAI,OAAO,OAAA,KAAY,MAAA,SAAkB,GAAA,CAAI,SAAA,EAAW,MAAM,OAAO,CAAA;AACrE,IAAA,MAAM,EAAA,GAAK,OAAO,QAAA,EAAS;AAC3B,IAAA,OAAO,IAAA,CAAK,SAA4B,KAAA,EAAO,CAAA,YAAA,EAAe,KAAK,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,EAAE,CAAA,CAAE,CAAA;AAAA,EACpF;AAAA;AAAA,EAGA,IAAI,cAAA,GAAqC;AACvC,IAAA,OAAO,IAAA,CAAK,eAAA;AAAA,EACd;AAAA,EAEA,MAAM,QAAA,CAAY,MAAA,EAAgB,IAAA,EAAc,IAAA,EAA4B;AAC1E,IAAA,MAAM,iBAAiB,IAAA,KAAS,MAAA,GAAY,MAAA,GAAY,IAAA,CAAK,UAAU,IAAI,CAAA;AAC3E,IAAA,IAAI,OAAA;AACJ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,IAAA,CAAK,aAAa,OAAA,EAAA,EAAW;AAC5D,MAAA,MAAM,IAAA,GAAO,IAAI,eAAA,EAAgB;AACjC,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,KAAK,KAAA,EAAM,EAAG,KAAK,UAAU,CAAA;AAC5D,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,GAAG,IAAA,CAAK,QAAQ,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI;AAAA,UACvD,MAAA;AAAA,UACA,OAAA,EAAS,EAAE,GAAG,IAAA,CAAK,QAAA,EAAS;AAAA,UAC5B,IAAA,EAAM,cAAA;AAAA,UACN,QAAQ,IAAA,CAAK;AAAA,SACd,CAAA;AACD,QAAA,IAAI,kBAAkB,GAAA,CAAI,MAAM,CAAA,IAAK,OAAA,GAAU,KAAK,WAAA,EAAa;AAG/D,UAAA,MAAM,IAAA,GAAO,gBAAA,CAAiB,GAAA,EAAK,OAAO,CAAA;AAE1C,UAAA,MAAM,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,KAAA,CAAS,CAAA;AACtC,UAAA,MAAM,MAAM,IAAI,CAAA;AAChB,UAAA;AAAA,QACF;AACA,QAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,QAAA,MAAM,MAAA,GAAkB,IAAA,GAAO,QAAA,CAAS,IAAI,CAAA,GAAI,KAAA,CAAA;AAChD,QAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,UAAA,MAAM,WACH,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,aAAa,MAAA,GAClD,MAAA,CAAQ,MAAA,CAAgC,OAAO,IAC/C,IAAA,KAAS,CAAA,KAAA,EAAQ,IAAI,MAAM,CAAA,CAAA,EAAI,IAAI,UAAU,CAAA,CAAA;AACnD,UAAA,MAAM,IAAI,cAAA,CAAe,OAAA,EAAS,GAAA,CAAI,QAAQ,MAAM,CAAA;AAAA,QACtD;AACA,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,IAAI,GAAA,YAAe,KAAA,IAAS,GAAA,CAAI,IAAA,KAAS,YAAA,EAAc;AACrD,UAAA,MAAM,IAAI,iBAAA,CAAkB,IAAA,CAAK,UAAU,CAAA;AAAA,QAC7C;AACA,QAAA,IAAI,GAAA,YAAe,gBAAgB,MAAM,GAAA;AACzC,QAAA,OAAA,GAAU,GAAA;AACV,QAAA,IAAI,OAAA,GAAU,KAAK,WAAA,EAAa;AAC9B,UAAA,MAAM,KAAA,CAAM,YAAA,CAAa,OAAO,CAAC,CAAA;AACjC,UAAA;AAAA,QACF;AACA,QAAA,MAAM,GAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF;AACA,IAAA,MAAM,OAAA,IAAW,IAAI,KAAA,CAAM,uBAAuB,CAAA;AAAA,EACpD;AACF;AAEA,SAAS,SAAS,CAAA,EAAoB;AACpC,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,CAAA;AAAA,EACT;AACF","file":"index.js","sourcesContent":["export class MnemoError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MnemoError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class MnemoHTTPError extends MnemoError {\n constructor(\n message: string,\n readonly status: number,\n readonly body?: unknown,\n ) {\n super(`[${status}] ${message}`)\n this.name = 'MnemoHTTPError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class MnemoTimeoutError extends MnemoError {\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`)\n this.name = 'MnemoTimeoutError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n","/**\n * Mnemo Memory client.\n *\n * Zero runtime dependencies — uses the global `fetch` (Node 18+, Bun, browsers,\n * Cloudflare Workers, Deno, etc).\n *\n * @example\n * ```ts\n * import { Mnemo } from '@mnemo/memory'\n *\n * const memory = new Mnemo({\n * apiKey: process.env.GETMNEMO_API_KEY!,\n * workspaceId: process.env.GETMNEMO_WORKSPACE_ID!,\n * })\n *\n * await memory.add({ content: 'User prefers Japanese rice.' })\n * const { hits } = await memory.search({ query: 'what rice does the user like?' })\n * ```\n */\n\nimport { MnemoHTTPError, MnemoTimeoutError } from './errors.js'\nimport type {\n ClientConfig,\n Memory,\n PaginatedMemories,\n SearchResponse,\n} from './types.js'\n\nconst DEFAULT_BASE_URL = 'https://api.mnemohq.com'\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst SDK_VERSION = '0.1.0'\nconst USER_AGENT = `@mnemo/memory/${SDK_VERSION}`\nconst DEFAULT_MAX_RETRIES = 3\nconst RETRY_BASE_DELAY_MS = 200\nconst RETRY_MAX_DELAY_MS = 5_000\n\n// Browsers reject `user-agent` as a forbidden header — setting it via fetch\n// throws or warns. Detect a browser-like environment so we can skip it there.\nconst IS_BROWSER_LIKE =\n typeof globalThis !== 'undefined' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (globalThis as any).window !== 'undefined' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (globalThis as any).document !== 'undefined'\n\nfunction retryDelayMs(attempt: number): number {\n const capped = Math.min(RETRY_BASE_DELAY_MS * 2 ** attempt, RETRY_MAX_DELAY_MS)\n // Full jitter.\n return Math.floor(Math.random() * capped)\n}\n\nfunction isRetryableStatus(status: number): boolean {\n // 501 Not Implemented is a permanent failure — retrying just wastes round-trips.\n if (status === 501) return false\n return status === 429 || (status >= 500 && status < 600)\n}\n\nfunction parseRetryAfterMs(headerValue: string | null): number | null {\n if (!headerValue) return null\n const trimmed = headerValue.trim()\n // Delta-seconds form.\n const seconds = Number(trimmed)\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(seconds * 1000, RETRY_MAX_DELAY_MS)\n }\n // HTTP-date form.\n const epoch = Date.parse(trimmed)\n if (!Number.isNaN(epoch)) {\n const delta = epoch - Date.now()\n return Math.max(0, Math.min(delta, RETRY_MAX_DELAY_MS))\n }\n return null\n}\n\nfunction delayForResponse(res: Response, attempt: number): number {\n const hint = parseRetryAfterMs(res.headers.get('retry-after'))\n return hint !== null ? hint : retryDelayMs(attempt)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport class Mnemo {\n readonly #baseUrl: string\n readonly #headers: Record<string, string>\n readonly #fetch: typeof fetch\n readonly #timeoutMs: number\n readonly #maxRetries: number\n readonly #defaultActorId: string | undefined\n\n constructor(cfg: ClientConfig) {\n if (!cfg.apiKey) throw new Error('Mnemo: apiKey is required')\n if (!cfg.workspaceId) throw new Error('Mnemo: workspaceId is required')\n this.#baseUrl = (cfg.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.#headers = {\n authorization: `Bearer ${cfg.apiKey}`,\n 'x-workspace-id': cfg.workspaceId,\n 'content-type': 'application/json',\n }\n // `user-agent` is on the forbidden header list in browsers — setting it\n // via fetch is silently dropped or throws. Send `x-getmnemo-client` as\n // an SDK identifier in browsers, and the standard User-Agent on Node.\n if (IS_BROWSER_LIKE) {\n this.#headers['x-getmnemo-client'] = USER_AGENT\n } else {\n this.#headers['user-agent'] = USER_AGENT\n }\n if (cfg.actorId) this.#headers['x-actor-id'] = cfg.actorId\n this.#defaultActorId = cfg.actorId\n this.#fetch = cfg.fetch ?? fetch\n this.#timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS\n this.#maxRetries = Math.max(0, cfg.maxRetries ?? DEFAULT_MAX_RETRIES)\n }\n\n async search(input: {\n query: string\n limit?: number\n actorId?: string\n }): Promise<SearchResponse> {\n return this.#request<SearchResponse>('POST', '/v1/search', {\n query: input.query,\n limit: input.limit ?? 8,\n ...(input.actorId !== undefined ? { actorId: input.actorId } : {}),\n })\n }\n\n async add(input: {\n content: string\n metadata?: Record<string, unknown>\n actorId?: string\n }): Promise<Memory> {\n return this.#request<Memory>('POST', '/v1/memories', {\n content: input.content,\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n ...(input.actorId !== undefined ? { actorId: input.actorId } : {}),\n })\n }\n\n async update(\n id: string,\n input: { content?: string; metadata?: Record<string, unknown> },\n ): Promise<Memory> {\n if (input.content === undefined && input.metadata === undefined) {\n throw new Error('Mnemo.update: at least one of content/metadata must be provided')\n }\n return this.#request<Memory>('PATCH', `/v1/memories/${encodeURIComponent(id)}`, input)\n }\n\n async get(id: string): Promise<Memory> {\n return this.#request<Memory>('GET', `/v1/memories/${encodeURIComponent(id)}`)\n }\n\n async delete(id: string): Promise<void> {\n await this.#request<unknown>('DELETE', `/v1/memories/${encodeURIComponent(id)}`)\n }\n\n async list(input?: {\n limit?: number\n cursor?: string\n actorId?: string\n }): Promise<PaginatedMemories> {\n const params = new URLSearchParams()\n if (input?.limit !== undefined) params.set('limit', String(input.limit))\n if (input?.cursor !== undefined) params.set('cursor', input.cursor)\n if (input?.actorId !== undefined) params.set('actorId', input.actorId)\n const qs = params.toString()\n return this.#request<PaginatedMemories>('GET', `/v1/memories${qs ? `?${qs}` : ''}`)\n }\n\n /** Echoed back for debugging — never sent to the wire. */\n get defaultActorId(): string | undefined {\n return this.#defaultActorId\n }\n\n async #request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const serializedBody = body === undefined ? undefined : JSON.stringify(body)\n let lastErr: unknown\n for (let attempt = 0; attempt <= this.#maxRetries; attempt++) {\n const ctrl = new AbortController()\n const timer = setTimeout(() => ctrl.abort(), this.#timeoutMs)\n try {\n const res = await this.#fetch(`${this.#baseUrl}${path}`, {\n method,\n headers: { ...this.#headers },\n body: serializedBody,\n signal: ctrl.signal,\n })\n if (isRetryableStatus(res.status) && attempt < this.#maxRetries) {\n // Capture Retry-After before draining; some runtimes invalidate\n // headers once the body is consumed.\n const wait = delayForResponse(res, attempt)\n // Drain body so the underlying connection can be reused.\n await res.text().catch(() => undefined)\n await sleep(wait)\n continue\n }\n const text = await res.text()\n const parsed: unknown = text ? safeJson(text) : undefined\n if (!res.ok) {\n const message =\n (parsed && typeof parsed === 'object' && 'message' in parsed\n ? String((parsed as { message: unknown }).message)\n : null) ?? `HTTP ${res.status} ${res.statusText}`\n throw new MnemoHTTPError(message, res.status, parsed)\n }\n return parsed as T\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new MnemoTimeoutError(this.#timeoutMs)\n }\n if (err instanceof MnemoHTTPError) throw err\n lastErr = err\n if (attempt < this.#maxRetries) {\n await sleep(retryDelayMs(attempt))\n continue\n }\n throw err\n } finally {\n clearTimeout(timer)\n }\n }\n throw lastErr ?? new Error('Mnemo: request failed')\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s)\n } catch {\n return s\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export class MnemoError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MnemoError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class MnemoHTTPError extends MnemoError {\n constructor(\n message: string,\n readonly status: number,\n readonly body?: unknown,\n ) {\n super(`[${status}] ${message}`)\n this.name = 'MnemoHTTPError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class MnemoTimeoutError extends MnemoError {\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`)\n this.name = 'MnemoTimeoutError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n","/**\n * Mnemo Memory client.\n *\n * Zero runtime dependencies — uses the global `fetch` (Node 18+, Bun, browsers,\n * Cloudflare Workers, Deno, etc).\n *\n * @example\n * ```ts\n * import { Mnemo } from 'getmnemo'\n *\n * const memory = new Mnemo({\n * apiKey: process.env.GETMNEMO_API_KEY!,\n * workspaceId: process.env.GETMNEMO_WORKSPACE_ID!,\n * })\n *\n * await memory.add({ content: 'User prefers Japanese rice.', containerTag: 'user:jane' })\n * const { results } = await memory.search({ q: 'what rice does the user like?', containerTag: 'user:jane' })\n * ```\n */\n\nimport { MnemoHTTPError, MnemoTimeoutError } from './errors.js'\nimport type {\n AddResponse,\n ClientConfig,\n Memory,\n PaginatedMemories,\n Scope,\n SearchResponse,\n} from './types.js'\n\nconst DEFAULT_BASE_URL = 'https://api.mnemohq.com'\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst SDK_VERSION = '0.2.0'\nconst DEFAULT_SEARCH_LIMIT = 8\nconst USER_AGENT = `getmnemo/${SDK_VERSION}`\nconst DEFAULT_MAX_RETRIES = 3\nconst RETRY_BASE_DELAY_MS = 200\nconst RETRY_MAX_DELAY_MS = 5_000\n\n// Browsers reject `user-agent` as a forbidden header — setting it via fetch\n// throws or warns. Detect a browser-like environment so we can skip it there.\nconst IS_BROWSER_LIKE =\n typeof globalThis !== 'undefined' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (globalThis as any).window !== 'undefined' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof (globalThis as any).document !== 'undefined'\n\nfunction retryDelayMs(attempt: number): number {\n const capped = Math.min(RETRY_BASE_DELAY_MS * 2 ** attempt, RETRY_MAX_DELAY_MS)\n // Full jitter.\n return Math.floor(Math.random() * capped)\n}\n\nfunction isRetryableStatus(status: number): boolean {\n // 501 Not Implemented is a permanent failure — retrying just wastes round-trips.\n if (status === 501) return false\n return status === 429 || (status >= 500 && status < 600)\n}\n\nfunction parseRetryAfterMs(headerValue: string | null): number | null {\n if (!headerValue) return null\n const trimmed = headerValue.trim()\n // Delta-seconds form.\n const seconds = Number(trimmed)\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(seconds * 1000, RETRY_MAX_DELAY_MS)\n }\n // HTTP-date form.\n const epoch = Date.parse(trimmed)\n if (!Number.isNaN(epoch)) {\n const delta = epoch - Date.now()\n return Math.max(0, Math.min(delta, RETRY_MAX_DELAY_MS))\n }\n return null\n}\n\nfunction delayForResponse(res: Response, attempt: number): number {\n const hint = parseRetryAfterMs(res.headers.get('retry-after'))\n return hint !== null ? hint : retryDelayMs(attempt)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport class Mnemo {\n readonly #baseUrl: string\n readonly #headers: Record<string, string>\n readonly #fetch: typeof fetch\n readonly #timeoutMs: number\n readonly #maxRetries: number\n readonly #defaultContainerTag: string | undefined\n\n constructor(cfg: ClientConfig) {\n if (!cfg.apiKey) throw new Error('Mnemo: apiKey is required')\n if (!cfg.workspaceId) throw new Error('Mnemo: workspaceId is required')\n this.#baseUrl = (cfg.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.#headers = {\n authorization: `Bearer ${cfg.apiKey}`,\n 'x-workspace-id': cfg.workspaceId,\n 'content-type': 'application/json',\n }\n // `user-agent` is on the forbidden header list in browsers — setting it\n // via fetch is silently dropped or throws. Send `x-getmnemo-client` as\n // an SDK identifier in browsers, and the standard User-Agent on Node.\n if (IS_BROWSER_LIKE) {\n this.#headers['x-getmnemo-client'] = USER_AGENT\n } else {\n this.#headers['user-agent'] = USER_AGENT\n }\n this.#defaultContainerTag = cfg.defaultContainerTag\n this.#fetch = cfg.fetch ?? fetch\n this.#timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS\n this.#maxRetries = Math.max(0, cfg.maxRetries ?? DEFAULT_MAX_RETRIES)\n }\n\n /**\n * Resolve the container for a call into the request fields the API expects.\n * A structured `scope` wins over a `containerTag` string; both fall back to\n * the constructor's `defaultContainerTag`. Throws if none is available.\n */\n #resolveContainer(\n method: 'add' | 'search',\n input: { containerTag?: string; scope?: Scope },\n ): { containerTag: string } | { scope: Scope } {\n if (input.scope) return { scope: input.scope }\n const tag = input.containerTag ?? this.#defaultContainerTag\n if (tag) return { containerTag: tag }\n throw new Error(\n `Mnemo.${method}: a container is required — pass containerTag (e.g. \"user:jane\") ` +\n 'or scope ({ type, id }) per call, or set defaultContainerTag on the client.',\n )\n }\n\n /**\n * Hybrid retrieval. Requires a container — pass `containerTag` (e.g.\n * `\"user:jane\"`) or `scope`, or set `defaultContainerTag` on the client.\n *\n * Sends `POST /v1/search` with body `{ q, limit, containerTag|scope }`.\n */\n async search(input: {\n q: string\n containerTag?: string\n scope?: Scope\n limit?: number\n searchMode?: string\n }): Promise<SearchResponse> {\n const container = this.#resolveContainer('search', input)\n return this.#request<SearchResponse>('POST', '/v1/search', {\n q: input.q,\n limit: input.limit ?? DEFAULT_SEARCH_LIMIT,\n ...(input.searchMode !== undefined ? { searchMode: input.searchMode } : {}),\n ...container,\n })\n }\n\n /**\n * Store an atomic fact. Requires a container — pass `containerTag` (e.g.\n * `\"user:jane\"`) or `scope`, or set `defaultContainerTag` on the client.\n *\n * Sends `POST /v1/memories` with body\n * `{ items: [{ content, metadata? }], containerTag|scope }`.\n */\n async add(input: {\n content: string\n containerTag?: string\n scope?: Scope\n metadata?: Record<string, unknown>\n }): Promise<AddResponse> {\n const container = this.#resolveContainer('add', input)\n return this.#request<AddResponse>('POST', '/v1/memories', {\n items: [\n {\n content: input.content,\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n },\n ],\n ...container,\n })\n }\n\n /**\n * Patch an existing memory by id.\n * Sends `PATCH /v1/memories/{memoryId}` with body `UpdateMemoryDto`\n * `{ content?, memoryType?, metadata?, source? }` (none required).\n */\n async update(\n memoryId: string,\n input: {\n content?: string\n memoryType?: string\n metadata?: Record<string, unknown>\n source?: string\n },\n ): Promise<Memory> {\n if (\n input.content === undefined &&\n input.memoryType === undefined &&\n input.metadata === undefined &&\n input.source === undefined\n ) {\n throw new Error(\n 'Mnemo.update: at least one of content/memoryType/metadata/source must be provided',\n )\n }\n return this.#request<Memory>(\n 'PATCH',\n `/v1/memories/${encodeURIComponent(memoryId)}`,\n input,\n )\n }\n\n /** Fetch a single memory by id. Sends `GET /v1/memories/{memoryId}`. */\n async get(memoryId: string): Promise<Memory> {\n return this.#request<Memory>('GET', `/v1/memories/${encodeURIComponent(memoryId)}`)\n }\n\n /** Remove a memory by id. Sends `DELETE /v1/memories/{memoryId}`. */\n async delete(memoryId: string): Promise<void> {\n await this.#request<unknown>(\n 'DELETE',\n `/v1/memories/${encodeURIComponent(memoryId)}`,\n )\n }\n\n /**\n * Cursor-paginated list of memories, optionally filtered by container.\n * Sends `GET /v1/memories` with query\n * `limit?, cursor?, scopeType?, scopeId?, containerTag?`.\n */\n async list(input?: {\n containerTag?: string\n limit?: number\n cursor?: string\n scopeType?: string\n scopeId?: string\n }): Promise<PaginatedMemories> {\n const params = new URLSearchParams()\n if (input?.limit !== undefined) params.set('limit', String(input.limit))\n if (input?.cursor !== undefined) params.set('cursor', input.cursor)\n if (input?.scopeType !== undefined) params.set('scopeType', input.scopeType)\n if (input?.scopeId !== undefined) params.set('scopeId', input.scopeId)\n if (input?.containerTag !== undefined) params.set('containerTag', input.containerTag)\n const qs = params.toString()\n return this.#request<PaginatedMemories>('GET', `/v1/memories${qs ? `?${qs}` : ''}`)\n }\n\n /** Echoed back for debugging — never sent to the wire. */\n get defaultContainerTag(): string | undefined {\n return this.#defaultContainerTag\n }\n\n async #request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const serializedBody = body === undefined ? undefined : JSON.stringify(body)\n let lastErr: unknown\n for (let attempt = 0; attempt <= this.#maxRetries; attempt++) {\n const ctrl = new AbortController()\n const timer = setTimeout(() => ctrl.abort(), this.#timeoutMs)\n try {\n const res = await this.#fetch(`${this.#baseUrl}${path}`, {\n method,\n headers: { ...this.#headers },\n body: serializedBody,\n signal: ctrl.signal,\n })\n if (isRetryableStatus(res.status) && attempt < this.#maxRetries) {\n // Capture Retry-After before draining; some runtimes invalidate\n // headers once the body is consumed.\n const wait = delayForResponse(res, attempt)\n // Drain body so the underlying connection can be reused.\n await res.text().catch(() => undefined)\n await sleep(wait)\n continue\n }\n const text = await res.text()\n const parsed: unknown = text ? safeJson(text) : undefined\n if (!res.ok) {\n const message =\n (parsed && typeof parsed === 'object' && 'message' in parsed\n ? String((parsed as { message: unknown }).message)\n : null) ?? `HTTP ${res.status} ${res.statusText}`\n throw new MnemoHTTPError(message, res.status, parsed)\n }\n return parsed as T\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new MnemoTimeoutError(this.#timeoutMs)\n }\n if (err instanceof MnemoHTTPError) throw err\n lastErr = err\n if (attempt < this.#maxRetries) {\n await sleep(retryDelayMs(attempt))\n continue\n }\n throw err\n } finally {\n clearTimeout(timer)\n }\n }\n throw lastErr ?? new Error('Mnemo: request failed')\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s)\n } catch {\n return s\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getmnemo",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Official TypeScript / JavaScript SDK for Mnemo Memory — long-term memory infrastructure for AI agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,7 +28,8 @@
28
28
  "lint": "eslint src",
29
29
  "typecheck": "tsc --noEmit",
30
30
  "test": "vitest run",
31
- "test:watch": "vitest"
31
+ "test:watch": "vitest",
32
+ "smoke": "node scripts/prod-smoke.mjs"
32
33
  },
33
34
  "keywords": [
34
35
  "mnemo",
@@ -45,7 +46,7 @@
45
46
  "url": "https://github.com/ledgermem/getmnemo-js.git"
46
47
  },
47
48
  "bugs": {
48
- "url": "https://github.com/getmnemo/getmnemo-js/issues"
49
+ "url": "https://github.com/ledgermem/getmnemo-js/issues"
49
50
  },
50
51
  "dependencies": {},
51
52
  "devDependencies": {