@arkimedehq/recordare-client 0.3.0 → 0.4.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
@@ -38,6 +38,8 @@ await rc.ingestRoom({ conversation: { externalId: 'kitchen-1' }, recipients: ['u
38
38
  messages: [{ externalId: 'k1', speakerRef: 'c', content: 'Mi hanno preso in squadra!', sentAt: new Date().toISOString() }] });
39
39
  // Mode (only while the memory is empty, else MemoryNotEmptyError) and the first person's gender, from the profile.
40
40
  await rc.updateMe('user-42', { gender: 'feminine' });
41
+ // The account is deleted: erase the whole memory (a key with the erase scope; the memory id confirms which).
42
+ await rc.eraseMemory('user-42', (await rc.me('user-42')).memoryId);
41
43
 
42
44
  // Recall: one MCP session per user AND conversation (evidence of writes, the current turn), both bound in code.
43
45
  const tools = await rc.mcp.listTools('user-42', 'chat-1'); // build the agent's tools from these schemas
@@ -104,10 +106,9 @@ Everything else is here, the same for every client.
104
106
  deletions propagate, the name, mode and gender follow the platform, own content is marked, a source is learned in
105
107
  parts, listed and forgotten, recall over MCP carries the user and the conversation. Type checks there fail the build when this contract drifts from the service's schemas.
106
108
 
107
- ## Synced copy (Arkimede)
108
- Arkimede still uses a copy of the sources instead of the npm package: `scripts/sync-to.sh <dir>` copies them into a
109
- host repository with a header naming the source commit (Arkimede: `backend/src/recordare/client/`). The host never
110
- edits them; change the library here and sync again. Switching to the npm package only changes the imports.
109
+ ## CommonJS hosts
110
+ The package is ESM only. A CommonJS host (Arkimede's NestJS backend) loads it with `require`, which Node supports for
111
+ ES modules from 20.19 / 22.12; with TypeScript, `"module": "nodenext"` (5.8 or later) type-checks that import.
111
112
 
112
113
  ## Development
113
114
  `npm ci`, `npm run typecheck`, `npm test`, `npm run build` (Node ≥ 20).
package/README_it.md CHANGED
@@ -38,6 +38,8 @@ await rc.ingestRoom({ conversation: { externalId: 'kitchen-1' }, recipients: ['u
38
38
  messages: [{ externalId: 'k1', speakerRef: 'c', content: 'Mi hanno preso in squadra!', sentAt: new Date().toISOString() }] });
39
39
  // Modo (solo finché la memoria è vuota, altrimenti MemoryNotEmptyError) e genere della prima persona, dal profilo.
40
40
  await rc.updateMe('user-42', { gender: 'feminine' });
41
+ // L'account viene eliminato: si cancella l'intera memoria (una chiave con lo scope erase; l'id della memoria conferma quale).
42
+ await rc.eraseMemory('user-42', (await rc.me('user-42')).memoryId);
41
43
 
42
44
  // Richiamo: una sessione MCP per utente E conversazione (prove delle scritture, il turno corrente), entrambi fissati nel codice.
43
45
  const tools = await rc.mcp.listTools('user-42', 'chat-1'); // gli strumenti dell'agente nascono da questi schemi
@@ -108,11 +110,10 @@ volta sola, le cancellazioni si propagano, nome, modo e genere seguono la piatta
108
110
  fonte viene imparata a parti, elencata e dimenticata, il richiamo via MCP porta l'utente e la conversazione. I controlli di tipo lì fanno fallire la build se questo contratto si
109
111
  allontana dagli schemi del servizio.
110
112
 
111
- ## Copia sincronizzata (Arkimede)
112
- Arkimede usa ancora una copia dei sorgenti invece del pacchetto npm: `scripts/sync-to.sh <dir>` copia i sorgenti nel
113
- repository dell'host con un'intestazione che indica il commit di origine (Arkimede: `backend/src/recordare/client/`).
114
- L'host non li modifica mai; si cambia la libreria qui e si sincronizza di nuovo. Passare al pacchetto npm cambia solo
115
- gli import.
113
+ ## Host CommonJS
114
+ Il pacchetto è solo ESM. Un host CommonJS (il backend NestJS di Arkimede) lo carica con `require`, che Node supporta per
115
+ i moduli ES dalla 20.19 / 22.12; con TypeScript, `"module": "nodenext"` (5.8 o successivo) controlla i tipi di
116
+ quell'import.
116
117
 
117
118
  ## Sviluppo
118
119
  `npm ci`, `npm run typecheck`, `npm test`, `npm run build` (Node ≥ 20).
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Digest, type Episode, type EpisodeDetail, type EpisodeQuery, type ExportJob, type Fact, type FactDetail, type HolderIdentities, type IngestRequest, type IngestResult, type RoomRequest, type RoomResult, type LearnSource, type LinkCode, type LearnSourceResult, type Me, type MemorySettings, type MeSettings, type MemoryContext, type Note, type NoteChange, type NoteDetail, type PlanStatus, type Precision, type Source, type Usage } from './contract.js';
1
+ import { type Cards, type ContextOptions, type Digest, type Episode, type Erased, type EpisodeDetail, type EpisodeQuery, type ExportJob, type Fact, type FactDetail, type HolderIdentities, type IngestRequest, type IngestResult, type RoomRequest, type RoomResult, type LearnSource, type LinkCode, type LearnSourceResult, type Me, type MemorySettings, type MeSettings, type MemoryContext, type Note, type NoteChange, type NoteDetail, type PlanStatus, type Precision, type Source, type Usage } from './contract.js';
2
2
  import { type ClientOptions, Http } from './http.js';
3
3
  import { RecordareMcp, type McpOptions } from './mcp.js';
4
4
  /**
@@ -16,6 +16,11 @@ export declare class RecordareClient {
16
16
  me(user: string): Promise<Me>;
17
17
  /** The memory's settings from the platform: the name (keep it in sync on every rename), the mode and the gender. */
18
18
  updateMe(user: string, settings: MeSettings): Promise<void>;
19
+ /**
20
+ * Erases the whole memory behind `user`, at once and for good (needs the `erase` scope): everything it holds goes.
21
+ * `memoryId` (from `me`) confirms which memory; a user without a memory answers 404, none is created.
22
+ */
23
+ eraseMemory(user: string, memoryId: string): Promise<Erased>;
19
24
  /** A one-time code for the holder to send from a channel (D51; personal memories only, else 409). */
20
25
  linkCode(user: string): Promise<LinkCode>;
21
26
  /** The identities that speak as the memory's "I" and the claims waiting for confirmation (D51). */
@@ -39,14 +44,14 @@ export declare class RecordareClient {
39
44
  /**
40
45
  * The memories relevant to `query` (the message about to be answered) as a fenced block to append to the prompt — or
41
46
  * null. Append it at the end of the system prompt (it changes every turn: keep the stable part first for prompt
42
- * caching); never store it as a chat message.
47
+ * caching); never store it as a chat message. `card`: open it with the self card (who I am), off by default.
43
48
  */
44
- context(user: string, conversation: string, query: string): Promise<MemoryContext>;
49
+ context(user: string, conversation: string, query: string, opts?: ContextOptions): Promise<MemoryContext>;
45
50
  /**
46
51
  * Stores the turn and returns the memory context for it in one round trip (needs the `ingest` and `read` scopes):
47
52
  * the context is computed for the ingested conversation, from `query` or else the turn's last user message.
48
53
  */
49
- contextWithTurn(user: string, turn: IngestRequest, query?: string): Promise<MemoryContext>;
54
+ contextWithTurn(user: string, turn: IngestRequest, query?: string, opts?: ContextOptions): Promise<MemoryContext>;
50
55
  /** The conversation ended (session closed, /new): extraction runs now. A conversation Recordare never had is done. */
51
56
  endConversation(user: string, conversation: string): Promise<void>;
52
57
  /** An edited message: Recordare re-extracts what depended on it. */
@@ -102,6 +107,8 @@ export declare class RecordareClient {
102
107
  asOf?: string;
103
108
  includePending?: boolean;
104
109
  }): Promise<Fact[]>;
110
+ /** Who I am and who my contacts are: current facts and notes of the self and of each contact (no LLM). */
111
+ cards(user: string): Promise<Cards>;
105
112
  notes(user: string, query?: {
106
113
  category?: Note['category'];
107
114
  pinned?: boolean;
package/dist/client.js CHANGED
@@ -41,6 +41,13 @@ export class RecordareClient {
41
41
  throw err;
42
42
  }
43
43
  }
44
+ /**
45
+ * Erases the whole memory behind `user`, at once and for good (needs the `erase` scope): everything it holds goes.
46
+ * `memoryId` (from `me`) confirms which memory; a user without a memory answers 404, none is created.
47
+ */
48
+ eraseMemory(user, memoryId) {
49
+ return this.http.request('DELETE', `api/v1/me${qs({ confirm: memoryId })}`, { user });
50
+ }
44
51
  /** A one-time code for the holder to send from a channel (D51; personal memories only, else 409). */
45
52
  linkCode(user) {
46
53
  return this.http.request('POST', 'api/v1/me/link-codes', { user });
@@ -113,17 +120,17 @@ export class RecordareClient {
113
120
  /**
114
121
  * The memories relevant to `query` (the message about to be answered) as a fenced block to append to the prompt — or
115
122
  * null. Append it at the end of the system prompt (it changes every turn: keep the stable part first for prompt
116
- * caching); never store it as a chat message.
123
+ * caching); never store it as a chat message. `card`: open it with the self card (who I am), off by default.
117
124
  */
118
- context(user, conversation, query) {
119
- return this.http.request('POST', 'api/v1/context', { user, body: { query }, headers: { [CONVERSATION_HEADER]: conversation } });
125
+ context(user, conversation, query, opts = {}) {
126
+ return this.http.request('POST', 'api/v1/context', { user, body: { query, ...opts }, headers: { [CONVERSATION_HEADER]: conversation } });
120
127
  }
121
128
  /**
122
129
  * Stores the turn and returns the memory context for it in one round trip (needs the `ingest` and `read` scopes):
123
130
  * the context is computed for the ingested conversation, from `query` or else the turn's last user message.
124
131
  */
125
- contextWithTurn(user, turn, query) {
126
- return this.http.request('POST', 'api/v1/context', { user, body: { ingest: turn, ...(query ? { query } : {}) } });
132
+ contextWithTurn(user, turn, query, opts = {}) {
133
+ return this.http.request('POST', 'api/v1/context', { user, body: { ingest: turn, ...(query ? { query } : {}), ...opts } });
127
134
  }
128
135
  /** The conversation ended (session closed, /new): extraction runs now. A conversation Recordare never had is done. */
129
136
  async endConversation(user, conversation) {
@@ -193,6 +200,10 @@ export class RecordareClient {
193
200
  facts(user, query = {}) {
194
201
  return this.http.request('GET', `api/v1/facts${qs(query)}`, { user });
195
202
  }
203
+ /** Who I am and who my contacts are: current facts and notes of the self and of each contact (no LLM). */
204
+ cards(user) {
205
+ return this.http.request('GET', 'api/v1/cards', { user });
206
+ }
196
207
  notes(user, query = {}) {
197
208
  return this.http.request('GET', `api/v1/notes${qs(query)}`, { user });
198
209
  }
@@ -29,6 +29,19 @@ export interface Me {
29
29
  via: string;
30
30
  scopes: string[];
31
31
  }
32
+ /** `DELETE api/v1/me`: what erasing the whole memory took, by kind. */
33
+ export interface Erased {
34
+ erased: {
35
+ conversations: number;
36
+ messages: number;
37
+ episodes: number;
38
+ facts: number;
39
+ notes: number;
40
+ digests: number;
41
+ sources: number;
42
+ contacts: number;
43
+ };
44
+ }
32
45
  /** `PATCH api/v1/me`: the person's settings from their platform. */
33
46
  export interface MeSettings {
34
47
  /** Follows the platform's profile: send it again whenever the user renames themselves. */
@@ -392,6 +405,36 @@ export interface Source {
392
405
  episodeIds: string[];
393
406
  }
394
407
  /** `POST api/v1/context`: the memories relevant to the message a host is about to answer (WORK_PLAN 5.7). */
408
+ /** `POST api/v1/context` options. */
409
+ export interface ContextOptions {
410
+ /** Open the block with the self card — who I am: current facts and notes about me. Off by default. */
411
+ card?: boolean;
412
+ }
413
+ /** `GET api/v1/cards`: a person as the memory knows them now (WORK_PLAN 8.13). */
414
+ export interface Card {
415
+ personId: string;
416
+ name: string;
417
+ aliases: string[];
418
+ facts: Array<{
419
+ id: string;
420
+ key: string;
421
+ value: string | null;
422
+ validFrom: string | null;
423
+ }>;
424
+ notes: Array<{
425
+ id: string;
426
+ category: string;
427
+ content: string;
428
+ pinned: boolean;
429
+ pattern: boolean;
430
+ }>;
431
+ /** The day of the latest episode about this contact (null for the self). */
432
+ lastMentioned: string | null;
433
+ }
434
+ export interface Cards {
435
+ self: Card;
436
+ contacts: Card[];
437
+ }
395
438
  export interface MemoryContext {
396
439
  /** A fenced `<memory-context>` block to append to the prompt, or null (nothing relevant, or off). */
397
440
  block: string | null;
package/dist/mcp.js CHANGED
@@ -80,7 +80,7 @@ export class RecordareMcp {
80
80
  },
81
81
  requestInit: { headers: { ...this.http.headers(user), [CONVERSATION_HEADER]: conversation } },
82
82
  });
83
- const client = new Client(this.options.clientInfo ?? { name: 'recordare-client', version: '0.3.0' });
83
+ const client = new Client(this.options.clientInfo ?? { name: 'recordare-client', version: '0.4.0' });
84
84
  await client.connect(transport);
85
85
  return client;
86
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkimedehq/recordare-client",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Client library for Recordare, the memory service for agent platforms: identity, ingest, deletions, MCP recall.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "Andrea Genovese",
package/src/client.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // Copyright © 2026 Andrea Genovese
3
3
 
4
4
  import {
5
- CONVERSATION_HEADER, type Digest, type Episode, type EpisodeDetail, type EpisodeQuery, type ExportJob, type Fact, type FactDetail,
5
+ type Cards, type ContextOptions, CONVERSATION_HEADER, type Digest, type Episode, type Erased, type EpisodeDetail, type EpisodeQuery, type ExportJob, type Fact, type FactDetail,
6
6
  type HolderIdentities, type IngestRequest, type IngestResult, type RoomRequest, type RoomResult, type LearnSource, type LinkCode, type LearnSourceResult, MAX_MESSAGES_PER_REQUEST, type Me, type MemorySettings,
7
7
  type MeSettings, type MemoryContext, type Note, type NoteChange, type NoteDetail, type PlanStatus, type Precision, type Source,
8
8
  SOURCE_PART_BYTES, type Usage,
@@ -50,6 +50,14 @@ export class RecordareClient {
50
50
  }
51
51
  }
52
52
 
53
+ /**
54
+ * Erases the whole memory behind `user`, at once and for good (needs the `erase` scope): everything it holds goes.
55
+ * `memoryId` (from `me`) confirms which memory; a user without a memory answers 404, none is created.
56
+ */
57
+ eraseMemory(user: string, memoryId: string): Promise<Erased> {
58
+ return this.http.request<Erased>('DELETE', `api/v1/me${qs({ confirm: memoryId })}`, { user });
59
+ }
60
+
53
61
  /** A one-time code for the holder to send from a channel (D51; personal memories only, else 409). */
54
62
  linkCode(user: string): Promise<LinkCode> {
55
63
  return this.http.request<LinkCode>('POST', 'api/v1/me/link-codes', { user });
@@ -128,18 +136,18 @@ export class RecordareClient {
128
136
  /**
129
137
  * The memories relevant to `query` (the message about to be answered) as a fenced block to append to the prompt — or
130
138
  * null. Append it at the end of the system prompt (it changes every turn: keep the stable part first for prompt
131
- * caching); never store it as a chat message.
139
+ * caching); never store it as a chat message. `card`: open it with the self card (who I am), off by default.
132
140
  */
133
- context(user: string, conversation: string, query: string): Promise<MemoryContext> {
134
- return this.http.request<MemoryContext>('POST', 'api/v1/context', { user, body: { query }, headers: { [CONVERSATION_HEADER]: conversation } });
141
+ context(user: string, conversation: string, query: string, opts: ContextOptions = {}): Promise<MemoryContext> {
142
+ return this.http.request<MemoryContext>('POST', 'api/v1/context', { user, body: { query, ...opts }, headers: { [CONVERSATION_HEADER]: conversation } });
135
143
  }
136
144
 
137
145
  /**
138
146
  * Stores the turn and returns the memory context for it in one round trip (needs the `ingest` and `read` scopes):
139
147
  * the context is computed for the ingested conversation, from `query` or else the turn's last user message.
140
148
  */
141
- contextWithTurn(user: string, turn: IngestRequest, query?: string): Promise<MemoryContext> {
142
- return this.http.request<MemoryContext>('POST', 'api/v1/context', { user, body: { ingest: turn, ...(query ? { query } : {}) } });
149
+ contextWithTurn(user: string, turn: IngestRequest, query?: string, opts: ContextOptions = {}): Promise<MemoryContext> {
150
+ return this.http.request<MemoryContext>('POST', 'api/v1/context', { user, body: { ingest: turn, ...(query ? { query } : {}), ...opts } });
143
151
  }
144
152
 
145
153
  /** The conversation ended (session closed, /new): extraction runs now. A conversation Recordare never had is done. */
@@ -226,6 +234,11 @@ export class RecordareClient {
226
234
  return this.http.request('GET', `api/v1/facts${qs(query)}`, { user });
227
235
  }
228
236
 
237
+ /** Who I am and who my contacts are: current facts and notes of the self and of each contact (no LLM). */
238
+ cards(user: string): Promise<Cards> {
239
+ return this.http.request<Cards>('GET', 'api/v1/cards', { user });
240
+ }
241
+
229
242
  notes(user: string, query: { category?: Note['category']; pinned?: boolean; includePending?: boolean } = {}): Promise<Note[]> {
230
243
  return this.http.request('GET', `api/v1/notes${qs(query)}`, { user });
231
244
  }
package/src/contract.ts CHANGED
@@ -37,6 +37,11 @@ export interface Me {
37
37
  scopes: string[];
38
38
  }
39
39
 
40
+ /** `DELETE api/v1/me`: what erasing the whole memory took, by kind. */
41
+ export interface Erased {
42
+ erased: { conversations: number; messages: number; episodes: number; facts: number; notes: number; digests: number; sources: number; contacts: number };
43
+ }
44
+
40
45
  /** `PATCH api/v1/me`: the person's settings from their platform. */
41
46
  export interface MeSettings {
42
47
  /** Follows the platform's profile: send it again whenever the user renames themselves. */
@@ -335,6 +340,28 @@ export interface Source {
335
340
  }
336
341
 
337
342
  /** `POST api/v1/context`: the memories relevant to the message a host is about to answer (WORK_PLAN 5.7). */
343
+ /** `POST api/v1/context` options. */
344
+ export interface ContextOptions {
345
+ /** Open the block with the self card — who I am: current facts and notes about me. Off by default. */
346
+ card?: boolean;
347
+ }
348
+
349
+ /** `GET api/v1/cards`: a person as the memory knows them now (WORK_PLAN 8.13). */
350
+ export interface Card {
351
+ personId: string;
352
+ name: string;
353
+ aliases: string[];
354
+ facts: Array<{ id: string; key: string; value: string | null; validFrom: string | null }>;
355
+ notes: Array<{ id: string; category: string; content: string; pinned: boolean; pattern: boolean }>;
356
+ /** The day of the latest episode about this contact (null for the self). */
357
+ lastMentioned: string | null;
358
+ }
359
+
360
+ export interface Cards {
361
+ self: Card;
362
+ contacts: Card[];
363
+ }
364
+
338
365
  export interface MemoryContext {
339
366
  /** A fenced `<memory-context>` block to append to the prompt, or null (nothing relevant, or off). */
340
367
  block: string | null;
package/src/mcp.ts CHANGED
@@ -99,7 +99,7 @@ export class RecordareMcp {
99
99
  },
100
100
  requestInit: { headers: { ...this.http.headers(user), [CONVERSATION_HEADER]: conversation } },
101
101
  });
102
- const client = new Client(this.options.clientInfo ?? { name: 'recordare-client', version: '0.3.0' });
102
+ const client = new Client(this.options.clientInfo ?? { name: 'recordare-client', version: '0.4.0' });
103
103
  await client.connect(transport);
104
104
  return client;
105
105
  }