@sentry/junior-memory 0.118.0 → 0.120.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.
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { type MemoryDb, type MemoryRecord } from "./store";
3
+ import type { MemoryRuntimeContext } from "./types";
4
+ declare const personalMemoryCursorSchema: z.ZodObject<{
5
+ createdAtMs: z.ZodNumber;
6
+ id: z.ZodString;
7
+ }, z.core.$strict>;
8
+ declare const personalMemoryPageInputSchema: z.ZodObject<{
9
+ cursor: z.ZodOptional<z.ZodObject<{
10
+ createdAtMs: z.ZodNumber;
11
+ id: z.ZodString;
12
+ }, z.core.$strict>>;
13
+ limit: z.ZodNumber;
14
+ query: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$strict>;
16
+ export type PersonalMemoryCursor = z.output<typeof personalMemoryCursorSchema>;
17
+ export type PersonalMemoryPageInput = z.output<typeof personalMemoryPageInputSchema>;
18
+ export interface PersonalMemoryPage {
19
+ memories: MemoryRecord[];
20
+ nextCursor?: PersonalMemoryCursor;
21
+ }
22
+ /** Expected failure when a viewer does not own the requested memory. */
23
+ export declare class PersonalMemoryNotFoundError extends Error {
24
+ constructor();
25
+ }
26
+ /** Viewer-scoped personal memory operations shared by dashboard and REST. */
27
+ export interface PersonalMemoryCollection {
28
+ /** Archive one exact personal memory owned by a linked actor. */
29
+ archive(id: string): Promise<MemoryRecord>;
30
+ /** Read one exact personal memory owned by a linked actor. */
31
+ get(id: string): Promise<MemoryRecord>;
32
+ /** List one stable page across every linked personal scope. */
33
+ list(input: PersonalMemoryPageInput): Promise<PersonalMemoryPage>;
34
+ }
35
+ /** Build storage operations for every personal scope linked to one viewer. */
36
+ export declare function createPersonalMemoryCollection(db: MemoryDb, runtimeContexts: MemoryRuntimeContext[], options?: {
37
+ now?: () => number;
38
+ }): PersonalMemoryCollection;
39
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { PluginUserPageActor } from "@sentry/junior-plugin-api";
2
+ import type { MemoryDb, MemoryRecord } from "./store";
3
+ export interface ViewerMemoryPage {
4
+ memories: MemoryRecord[];
5
+ nextCursor?: string;
6
+ }
7
+ export interface ViewerMemoryPageInput {
8
+ cursor?: string;
9
+ limit: number;
10
+ query?: string;
11
+ }
12
+ export declare class InvalidMemoryCursorError extends Error {
13
+ constructor();
14
+ }
15
+ export { PersonalMemoryNotFoundError } from "./personal-store";
16
+ /** Build personal-memory operations authorized by a viewer's linked actors. */
17
+ export declare function createViewerMemories(db: MemoryDb, actors: PluginUserPageActor[]): {
18
+ archive(id: string): Promise<MemoryRecord>;
19
+ get(id: string): Promise<MemoryRecord>;
20
+ list(input: ViewerMemoryPageInput): Promise<ViewerMemoryPage>;
21
+ };
package/dist/plugin.d.ts CHANGED
@@ -3,6 +3,5 @@ export interface MemoryPluginOptions {
3
3
  /** Maximum cosine distance for vector recall candidates. Defaults to MEMORY_RECALL_MAX_VECTOR_DISTANCE env or 0.45. */
4
4
  recallMaxVectorDistance?: number;
5
5
  }
6
- /** Create Junior's long-term memory plugin registration. */
7
- export declare function createMemoryPlugin(options?: MemoryPluginOptions): import("@sentry/junior-plugin-api").PluginRegistration;
8
- export declare const memoryPlugin: import("@sentry/junior-plugin-api").PluginRegistration;
6
+ /** Register Junior's long-term memory plugin. */
7
+ export declare function memoryPlugin(options?: MemoryPluginOptions): import("@sentry/junior-plugin-api").PluginRegistration;
@@ -0,0 +1,20 @@
1
+ import type { MemoryRecord } from "./store";
2
+ export interface MemoryMatch {
3
+ exactIdentifier: boolean;
4
+ lexical?: {
5
+ rank: number;
6
+ };
7
+ memory: MemoryRecord;
8
+ sourceKey: string;
9
+ vector?: {
10
+ rank: number;
11
+ };
12
+ }
13
+ /**
14
+ * Keep exact structured-identifier matches in the strongest relevance tier,
15
+ * then fuse lexical and vector ranks without comparing provider raw scores.
16
+ */
17
+ export declare function rankMemoryMatches(matches: MemoryMatch[], options: {
18
+ channelPrefix?: string;
19
+ nowMs: number;
20
+ }): MemoryMatch[];
package/dist/recall.d.ts CHANGED
@@ -1,10 +1,13 @@
1
- import { type UserPromptContribution, type Actor, type Source } from "@sentry/junior-plugin-api";
1
+ import { type UserPromptContribution, type Actor, type PluginLogger, type Source } from "@sentry/junior-plugin-api";
2
2
  import { z } from "zod";
3
+ import type { MemoryAgent } from "./agent";
3
4
  import { type MemoryDb, type MemoryEmbeddingProvider } from "./store";
4
5
  export interface MemoryRecallContext {
6
+ agent: Pick<MemoryAgent, "selectRelevantMemories">;
5
7
  conversationId?: string;
6
8
  db: MemoryDb;
7
9
  embedder?: MemoryEmbeddingProvider;
10
+ log: PluginLogger;
8
11
  /** Maximum cosine distance for vector recall. Passed through to the memory store. */
9
12
  maxVectorDistance?: number;
10
13
  actor?: Actor;
package/dist/store.d.ts CHANGED
@@ -1,8 +1,10 @@
1
+ import { type SQL } from "drizzle-orm";
1
2
  import type { PgDatabase } from "drizzle-orm/pg-core";
2
3
  import type { PgQueryResultHKT } from "drizzle-orm/pg-core/session";
3
4
  import { z } from "zod";
4
5
  import * as memorySqlSchema from "./db/schema";
5
6
  import { type MemoryRuntimeContext } from "./types";
7
+ import { type ResolvedMemoryScope } from "./scope";
6
8
  export type MemoryDb = PgDatabase<PgQueryResultHKT, typeof memorySqlSchema>;
7
9
  declare const createMemoryInputSchema: z.ZodObject<{
8
10
  content: z.ZodString;
@@ -168,9 +170,32 @@ export interface MemoryStore {
168
170
  listMemories(input: ListMemoriesInput): Promise<MemoryRecord[]>;
169
171
  /** List active personal memories owned by the current actor. */
170
172
  listPersonalMemories(input: ListMemoriesInput): Promise<MemoryRecord[]>;
173
+ /**
174
+ * Retrieve a broad relevance-ranked candidate window for automatic recall.
175
+ * Prompt admission remains owned by the recall boundary.
176
+ */
177
+ recallMemories(input: SearchMemoriesInput): Promise<MemoryRecord[]>;
171
178
  /** Search active memories visible in the current runtime context. */
172
179
  searchMemories(input: SearchMemoriesInput): Promise<MemoryRecord[]>;
173
180
  }
181
+ /** Parse one SQL row into the public memory record projection. */
182
+ /** Parse one SQL row into the public memory projection. */
183
+ export declare function parseMemoryRow(row: unknown): MemoryRecord;
184
+ /** Build the active-row predicate for already-authorized memory scopes. */
185
+ export declare function activeVisiblePredicate(args: {
186
+ nowMs: number;
187
+ scopes: ResolvedMemoryScope[];
188
+ }): SQL | undefined;
189
+ /**
190
+ * Archive a bounded batch of expired active rows and remove their derived vectors.
191
+ */
192
+ export declare function archiveExpiredMemoryBatch(args: {
193
+ db: MemoryDb;
194
+ idempotencyKey?: string;
195
+ limit?: number;
196
+ nowMs: number;
197
+ scopes: ResolvedMemoryScope[];
198
+ }): Promise<ArchiveExpiredMemoriesResult>;
174
199
  /** Create a context-bound SQL-backed store for explicit memory operations. */
175
200
  export declare function createMemoryStore(db: MemoryDb, context: MemoryRuntimeContext, options?: MemoryStoreOptions): MemoryStore;
176
201
  export {};
@@ -1,4 +1,4 @@
1
- /** Project actor-owned memories into Junior's core-rendered user page. */
1
+ /** Project viewer-owned memories into Junior's core-rendered user page. */
2
2
  import type { PluginUserPageDefinition } from "@sentry/junior-plugin-api";
3
- /** Create the read-only personal Memories dashboard page. */
3
+ /** Create the interactive personal Memories dashboard page. */
4
4
  export declare function createMemoryUserPage(): PluginUserPageDefinition;
@@ -0,0 +1 @@
1
+ CREATE INDEX "junior_memory_memories_search_idx" ON "junior_memory_memories" USING gin (to_tsvector('english', "content")) WHERE "junior_memory_memories"."archived_at_ms" IS NULL AND "junior_memory_memories"."superseded_at_ms" IS NULL AND "junior_memory_memories"."superseded_by_id" IS NULL;
@@ -0,0 +1,364 @@
1
+ {
2
+ "id": "6948ea3f-2e44-49ce-aa94-8a993ce61cec",
3
+ "prevId": "e915b537-996a-488c-a828-7cf4da06be44",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.junior_memory_embeddings": {
8
+ "name": "junior_memory_embeddings",
9
+ "schema": "",
10
+ "columns": {
11
+ "memory_id": {
12
+ "name": "memory_id",
13
+ "type": "text",
14
+ "primaryKey": true,
15
+ "notNull": true
16
+ },
17
+ "provider": {
18
+ "name": "provider",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true
22
+ },
23
+ "model": {
24
+ "name": "model",
25
+ "type": "text",
26
+ "primaryKey": false,
27
+ "notNull": true
28
+ },
29
+ "dimensions": {
30
+ "name": "dimensions",
31
+ "type": "integer",
32
+ "primaryKey": false,
33
+ "notNull": true
34
+ },
35
+ "metric": {
36
+ "name": "metric",
37
+ "type": "text",
38
+ "primaryKey": false,
39
+ "notNull": true
40
+ },
41
+ "content_hash": {
42
+ "name": "content_hash",
43
+ "type": "text",
44
+ "primaryKey": false,
45
+ "notNull": true
46
+ },
47
+ "embedding": {
48
+ "name": "embedding",
49
+ "type": "vector(1536)",
50
+ "primaryKey": false,
51
+ "notNull": true
52
+ },
53
+ "created_at_ms": {
54
+ "name": "created_at_ms",
55
+ "type": "bigint",
56
+ "primaryKey": false,
57
+ "notNull": true
58
+ }
59
+ },
60
+ "indexes": {
61
+ "junior_memory_embeddings_model_idx": {
62
+ "name": "junior_memory_embeddings_model_idx",
63
+ "columns": [
64
+ {
65
+ "expression": "provider",
66
+ "isExpression": false,
67
+ "asc": true,
68
+ "nulls": "last"
69
+ },
70
+ {
71
+ "expression": "model",
72
+ "isExpression": false,
73
+ "asc": true,
74
+ "nulls": "last"
75
+ },
76
+ {
77
+ "expression": "dimensions",
78
+ "isExpression": false,
79
+ "asc": true,
80
+ "nulls": "last"
81
+ },
82
+ {
83
+ "expression": "metric",
84
+ "isExpression": false,
85
+ "asc": true,
86
+ "nulls": "last"
87
+ }
88
+ ],
89
+ "isUnique": false,
90
+ "concurrently": false,
91
+ "method": "btree",
92
+ "with": {}
93
+ }
94
+ },
95
+ "foreignKeys": {
96
+ "junior_memory_embeddings_memory_id_junior_memory_memories_id_fk": {
97
+ "name": "junior_memory_embeddings_memory_id_junior_memory_memories_id_fk",
98
+ "tableFrom": "junior_memory_embeddings",
99
+ "tableTo": "junior_memory_memories",
100
+ "columnsFrom": ["memory_id"],
101
+ "columnsTo": ["id"],
102
+ "onDelete": "cascade",
103
+ "onUpdate": "no action"
104
+ }
105
+ },
106
+ "compositePrimaryKeys": {},
107
+ "uniqueConstraints": {},
108
+ "policies": {},
109
+ "checkConstraints": {
110
+ "junior_memory_embeddings_metric_check": {
111
+ "name": "junior_memory_embeddings_metric_check",
112
+ "value": "\"junior_memory_embeddings\".\"metric\" IN ('cosine')"
113
+ },
114
+ "junior_memory_embeddings_dimensions_check": {
115
+ "name": "junior_memory_embeddings_dimensions_check",
116
+ "value": "\"junior_memory_embeddings\".\"dimensions\" = 1536"
117
+ }
118
+ },
119
+ "isRLSEnabled": false
120
+ },
121
+ "public.junior_memory_memories": {
122
+ "name": "junior_memory_memories",
123
+ "schema": "",
124
+ "columns": {
125
+ "id": {
126
+ "name": "id",
127
+ "type": "text",
128
+ "primaryKey": true,
129
+ "notNull": true
130
+ },
131
+ "scope": {
132
+ "name": "scope",
133
+ "type": "text",
134
+ "primaryKey": false,
135
+ "notNull": true
136
+ },
137
+ "scope_key": {
138
+ "name": "scope_key",
139
+ "type": "text",
140
+ "primaryKey": false,
141
+ "notNull": true
142
+ },
143
+ "type": {
144
+ "name": "type",
145
+ "type": "text",
146
+ "primaryKey": false,
147
+ "notNull": true
148
+ },
149
+ "subject_type": {
150
+ "name": "subject_type",
151
+ "type": "text",
152
+ "primaryKey": false,
153
+ "notNull": true
154
+ },
155
+ "subject_key": {
156
+ "name": "subject_key",
157
+ "type": "text",
158
+ "primaryKey": false,
159
+ "notNull": false
160
+ },
161
+ "content": {
162
+ "name": "content",
163
+ "type": "text",
164
+ "primaryKey": false,
165
+ "notNull": true
166
+ },
167
+ "source_platform": {
168
+ "name": "source_platform",
169
+ "type": "text",
170
+ "primaryKey": false,
171
+ "notNull": true
172
+ },
173
+ "source_key": {
174
+ "name": "source_key",
175
+ "type": "text",
176
+ "primaryKey": false,
177
+ "notNull": true
178
+ },
179
+ "idempotency_key": {
180
+ "name": "idempotency_key",
181
+ "type": "text",
182
+ "primaryKey": false,
183
+ "notNull": false
184
+ },
185
+ "observed_at_ms": {
186
+ "name": "observed_at_ms",
187
+ "type": "bigint",
188
+ "primaryKey": false,
189
+ "notNull": true
190
+ },
191
+ "created_at_ms": {
192
+ "name": "created_at_ms",
193
+ "type": "bigint",
194
+ "primaryKey": false,
195
+ "notNull": true
196
+ },
197
+ "expires_at_ms": {
198
+ "name": "expires_at_ms",
199
+ "type": "bigint",
200
+ "primaryKey": false,
201
+ "notNull": false
202
+ },
203
+ "superseded_at_ms": {
204
+ "name": "superseded_at_ms",
205
+ "type": "bigint",
206
+ "primaryKey": false,
207
+ "notNull": false
208
+ },
209
+ "superseded_by_id": {
210
+ "name": "superseded_by_id",
211
+ "type": "text",
212
+ "primaryKey": false,
213
+ "notNull": false
214
+ },
215
+ "archived_at_ms": {
216
+ "name": "archived_at_ms",
217
+ "type": "bigint",
218
+ "primaryKey": false,
219
+ "notNull": false
220
+ },
221
+ "archive_reason": {
222
+ "name": "archive_reason",
223
+ "type": "text",
224
+ "primaryKey": false,
225
+ "notNull": false
226
+ }
227
+ },
228
+ "indexes": {
229
+ "junior_memory_memories_visible_idx": {
230
+ "name": "junior_memory_memories_visible_idx",
231
+ "columns": [
232
+ {
233
+ "expression": "scope",
234
+ "isExpression": false,
235
+ "asc": true,
236
+ "nulls": "last"
237
+ },
238
+ {
239
+ "expression": "scope_key",
240
+ "isExpression": false,
241
+ "asc": true,
242
+ "nulls": "last"
243
+ },
244
+ {
245
+ "expression": "created_at_ms",
246
+ "isExpression": false,
247
+ "asc": false,
248
+ "nulls": "last"
249
+ },
250
+ {
251
+ "expression": "id",
252
+ "isExpression": false,
253
+ "asc": true,
254
+ "nulls": "last"
255
+ }
256
+ ],
257
+ "isUnique": false,
258
+ "where": "\"junior_memory_memories\".\"archived_at_ms\" IS NULL AND \"junior_memory_memories\".\"superseded_at_ms\" IS NULL AND \"junior_memory_memories\".\"superseded_by_id\" IS NULL",
259
+ "concurrently": false,
260
+ "method": "btree",
261
+ "with": {}
262
+ },
263
+ "junior_memory_memories_expiration_idx": {
264
+ "name": "junior_memory_memories_expiration_idx",
265
+ "columns": [
266
+ {
267
+ "expression": "expires_at_ms",
268
+ "isExpression": false,
269
+ "asc": true,
270
+ "nulls": "last"
271
+ }
272
+ ],
273
+ "isUnique": false,
274
+ "where": "\"junior_memory_memories\".\"archived_at_ms\" IS NULL AND \"junior_memory_memories\".\"expires_at_ms\" IS NOT NULL",
275
+ "concurrently": false,
276
+ "method": "btree",
277
+ "with": {}
278
+ },
279
+ "junior_memory_memories_search_idx": {
280
+ "name": "junior_memory_memories_search_idx",
281
+ "columns": [
282
+ {
283
+ "expression": "to_tsvector('english', \"content\")",
284
+ "asc": true,
285
+ "isExpression": true,
286
+ "nulls": "last"
287
+ }
288
+ ],
289
+ "isUnique": false,
290
+ "where": "\"junior_memory_memories\".\"archived_at_ms\" IS NULL AND \"junior_memory_memories\".\"superseded_at_ms\" IS NULL AND \"junior_memory_memories\".\"superseded_by_id\" IS NULL",
291
+ "concurrently": false,
292
+ "method": "gin",
293
+ "with": {}
294
+ },
295
+ "junior_memory_memories_idempotency_idx": {
296
+ "name": "junior_memory_memories_idempotency_idx",
297
+ "columns": [
298
+ {
299
+ "expression": "scope",
300
+ "isExpression": false,
301
+ "asc": true,
302
+ "nulls": "last"
303
+ },
304
+ {
305
+ "expression": "scope_key",
306
+ "isExpression": false,
307
+ "asc": true,
308
+ "nulls": "last"
309
+ },
310
+ {
311
+ "expression": "idempotency_key",
312
+ "isExpression": false,
313
+ "asc": true,
314
+ "nulls": "last"
315
+ }
316
+ ],
317
+ "isUnique": true,
318
+ "where": "\"junior_memory_memories\".\"idempotency_key\" IS NOT NULL AND \"junior_memory_memories\".\"archived_at_ms\" IS NULL AND \"junior_memory_memories\".\"superseded_at_ms\" IS NULL AND \"junior_memory_memories\".\"superseded_by_id\" IS NULL",
319
+ "concurrently": false,
320
+ "method": "btree",
321
+ "with": {}
322
+ }
323
+ },
324
+ "foreignKeys": {},
325
+ "compositePrimaryKeys": {},
326
+ "uniqueConstraints": {},
327
+ "policies": {},
328
+ "checkConstraints": {
329
+ "junior_memory_memories_scope_check": {
330
+ "name": "junior_memory_memories_scope_check",
331
+ "value": "\"junior_memory_memories\".\"scope\" IN ('personal', 'conversation')"
332
+ },
333
+ "junior_memory_memories_kind_check": {
334
+ "name": "junior_memory_memories_kind_check",
335
+ "value": "\"junior_memory_memories\".\"type\" IN (\n 'preference',\n 'procedure',\n 'knowledge'\n )"
336
+ },
337
+ "junior_memory_memories_subject_type_check": {
338
+ "name": "junior_memory_memories_subject_type_check",
339
+ "value": "\"junior_memory_memories\".\"subject_type\" IN ('user', 'conversation', 'general')"
340
+ },
341
+ "junior_memory_memories_subject_key_check": {
342
+ "name": "junior_memory_memories_subject_key_check",
343
+ "value": "(\"junior_memory_memories\".\"subject_type\" = 'general' AND \"junior_memory_memories\".\"subject_key\" IS NULL) OR (\"junior_memory_memories\".\"subject_type\" IN ('user', 'conversation') AND \"junior_memory_memories\".\"subject_key\" IS NOT NULL AND length(\"junior_memory_memories\".\"subject_key\") > 0)"
344
+ },
345
+ "junior_memory_memories_source_platform_check": {
346
+ "name": "junior_memory_memories_source_platform_check",
347
+ "value": "\"junior_memory_memories\".\"source_platform\" IN ('slack', 'local')"
348
+ }
349
+ },
350
+ "isRLSEnabled": false
351
+ }
352
+ },
353
+ "enums": {},
354
+ "schemas": {},
355
+ "sequences": {},
356
+ "roles": {},
357
+ "policies": {},
358
+ "views": {},
359
+ "_meta": {
360
+ "columns": {},
361
+ "schemas": {},
362
+ "tables": {}
363
+ }
364
+ }
@@ -36,6 +36,13 @@
36
36
  "when": 1782676852768,
37
37
  "tag": "0004_gigantic_celestials",
38
38
  "breakpoints": true
39
+ },
40
+ {
41
+ "idx": 5,
42
+ "version": "7",
43
+ "when": 1785293874649,
44
+ "tag": "0005_short_jamie_braddock",
45
+ "breakpoints": true
39
46
  }
40
47
  ]
41
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-memory",
3
- "version": "0.118.0",
3
+ "version": "0.120.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -27,7 +27,7 @@
27
27
  "commander": "^14.0.3",
28
28
  "drizzle-orm": "^0.45.2",
29
29
  "zod": "^4.4.3",
30
- "@sentry/junior-plugin-api": "0.118.0"
30
+ "@sentry/junior-plugin-api": "0.120.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^25.9.1",
package/src/agent.ts CHANGED
@@ -23,6 +23,28 @@ const memoryRejectReasonSchema = z.enum([
23
23
  "assistant_or_system_detail",
24
24
  "unsupported_scope",
25
25
  ]);
26
+ const memoryRecallCandidateSchema = z
27
+ .object({
28
+ content: z.string().min(1),
29
+ id: z.string().min(1),
30
+ })
31
+ .strict();
32
+ const memoryRecallInputSchema = z
33
+ .object({
34
+ candidates: z.array(memoryRecallCandidateSchema).min(1).max(20),
35
+ userRequest: z.string().min(1),
36
+ })
37
+ .strict();
38
+ const memoryRecallDecisionSchema = z
39
+ .object({
40
+ relevantIds: z
41
+ .array(z.string().min(1))
42
+ .max(20)
43
+ .describe(
44
+ "Candidate ids whose memories directly help with the current request, ordered by relevance.",
45
+ ),
46
+ })
47
+ .strict();
26
48
  const createMemoryRequestSchema = z
27
49
  .object({
28
50
  content: z.string().min(1),
@@ -169,6 +191,7 @@ type MemoryReviewResponse = z.output<typeof memoryReviewResponseSchema>;
169
191
  type ExtractMemoriesResponse = z.output<typeof extractMemoriesResponseSchema>;
170
192
 
171
193
  export type MemoryReview = z.output<typeof memoryReviewDecisionSchema>;
194
+ export type MemoryRecallInput = z.output<typeof memoryRecallInputSchema>;
172
195
 
173
196
  export type CreateMemoryRequest = z.output<typeof createMemoryRequestSchema>;
174
197
  export type ExtractSessionRequest = z.output<
@@ -177,6 +200,10 @@ export type ExtractSessionRequest = z.output<
177
200
  export type ExtractedMemory = z.output<typeof extractedMemoryResultSchema>;
178
201
 
179
202
  export interface MemoryAgent {
203
+ /** Select candidate memories that directly help with the current request. */
204
+ selectRelevantMemories(
205
+ request: MemoryRecallInput,
206
+ ): Promise<string[]> | string[];
180
207
  /** Classify a new preference against related active preferences. */
181
208
  adjudicateSupersession(
182
209
  request: MemorySupersessionInput,
@@ -203,6 +230,13 @@ const MEMORY_EXTRACTION_SYSTEM = [
203
230
  "Reject secrets, credentials, private or sensitive personal details, gossip, speculative claims about other people, assistant/system implementation details, vague references, and low-durability chatter.",
204
231
  "If no public, durable, self-contained memory remains after rewriting, return an empty memories array.",
205
232
  ].join("\n");
233
+ const MEMORY_RECALL_SYSTEM = [
234
+ "You are Junior's memory recall relevance agent.",
235
+ "Select only memories that would directly help answer the user's current request.",
236
+ "Reject memories that merely share a company, product family, repository vocabulary, programming language, or general engineering context.",
237
+ "Prefer specific matches on the exact repository, workflow, command, test, CI setup, project, or user preference being asked about.",
238
+ "An empty relevantIds array is correct when no candidate is directly helpful.",
239
+ ].join("\n");
206
240
  const MEMORY_PREFERENCE_ADJUDICATION_SYSTEM = [
207
241
  "You are Junior's memory preference adjudication agent.",
208
242
  "Classify how one new actor preference relates to existing active actor preferences.",
@@ -444,6 +478,22 @@ function sessionExtractionPrompt(request: ExtractSessionRequest): string {
444
478
  ].join("\n");
445
479
  }
446
480
 
481
+ function recallRelevancePrompt(request: MemoryRecallInput): string {
482
+ return [
483
+ "<memory-recall-input>",
484
+ "<user-request>",
485
+ escapeXml(request.userRequest),
486
+ "</user-request>",
487
+ "",
488
+ "<candidate-memories>",
489
+ escapeXml(JSON.stringify(request.candidates)),
490
+ "</candidate-memories>",
491
+ "",
492
+ "Return only ids from candidate-memories. Preserve the most relevant candidates first.",
493
+ "</memory-recall-input>",
494
+ ].join("\n");
495
+ }
496
+
447
497
  function preferenceAdjudicationPrompt(
448
498
  request: MemorySupersessionInput,
449
499
  ): string {
@@ -477,9 +527,23 @@ function preferenceAdjudicationPrompt(
477
527
  ].join("\n");
478
528
  }
479
529
 
480
- /** Create the memory-owned agent that reviews and extracts memory candidates. */
530
+ /** Create the memory-owned agent that reviews, extracts, and recalls memories. */
481
531
  export function createMemoryAgent(model: PluginModel): MemoryAgent {
482
532
  return {
533
+ async selectRelevantMemories(rawRequest) {
534
+ const request = memoryRecallInputSchema.parse(rawRequest);
535
+ const result = await model.completeObject({
536
+ schema: memoryRecallDecisionSchema,
537
+ system: MEMORY_RECALL_SYSTEM,
538
+ prompt: recallRelevancePrompt(request),
539
+ maxTokens: 400,
540
+ });
541
+ const decision = memoryRecallDecisionSchema.parse(result.object);
542
+ const candidateIds = new Set(request.candidates.map(({ id }) => id));
543
+ return [...new Set(decision.relevantIds)].filter((id) =>
544
+ candidateIds.has(id),
545
+ );
546
+ },
483
547
  async adjudicateSupersession(rawRequest) {
484
548
  const request = memorySupersessionInputSchema.parse(rawRequest);
485
549
  const result = await model.completeObject({