@sentry/junior-memory 0.78.0 → 0.80.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/dist/agent.d.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  import type { PluginModel } from "@sentry/junior-plugin-api";
2
2
  import { z } from "zod";
3
- declare const memoryTargetSchema: z.ZodEnum<{
4
- requester: "requester";
5
- conversation: "conversation";
6
- }>;
7
3
  declare const createMemoryRequestSchema: z.ZodObject<{
8
4
  content: z.ZodString;
9
5
  expiresAtMs: z.ZodOptional<z.ZodNumber>;
@@ -103,9 +99,10 @@ declare const extractSessionRequestSchema: z.ZodObject<{
103
99
  }, z.core.$strict>;
104
100
  declare const memoryReviewDecisionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
105
101
  decision: z.ZodLiteral<"store">;
106
- target: z.ZodEnum<{
107
- requester: "requester";
108
- conversation: "conversation";
102
+ kind: z.ZodEnum<{
103
+ preference: "preference";
104
+ procedure: "procedure";
105
+ knowledge: "knowledge";
109
106
  }>;
110
107
  content: z.ZodString;
111
108
  expiresAtMs: z.ZodOptional<z.ZodNumber>;
@@ -122,21 +119,27 @@ declare const memoryReviewDecisionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
122
119
  unsupported_scope: "unsupported_scope";
123
120
  }>;
124
121
  }, z.core.$strict>], "decision">;
125
- export type MemoryTarget = z.output<typeof memoryTargetSchema>;
122
+ declare const extractedMemoryResultSchema: z.ZodObject<{
123
+ content: z.ZodString;
124
+ expiresAtMs: z.ZodNullable<z.ZodNumber>;
125
+ kind: z.ZodEnum<{
126
+ preference: "preference";
127
+ procedure: "procedure";
128
+ knowledge: "knowledge";
129
+ }>;
130
+ }, z.core.$strict>;
126
131
  export type MemoryReview = z.output<typeof memoryReviewDecisionSchema>;
127
132
  export type CreateMemoryRequest = z.output<typeof createMemoryRequestSchema>;
128
133
  export type ExtractSessionRequest = z.output<typeof extractSessionRequestSchema>;
129
- export interface ExtractedMemory {
130
- content: string;
131
- expiresAtMs: number | null;
132
- target: MemoryTarget;
133
- }
134
+ export type ExtractedMemory = z.output<typeof extractedMemoryResultSchema>;
134
135
  export interface MemoryAgent {
135
136
  extractSessionMemories(request: ExtractSessionRequest): Promise<ExtractedMemory[]> | ExtractedMemory[];
136
137
  reviewCreateRequest(request: CreateMemoryRequest): Promise<MemoryReview> | MemoryReview;
137
138
  }
138
139
  /** Create the memory-owned agent that reviews and extracts memory candidates. */
139
140
  export declare function createMemoryAgent(model: PluginModel): MemoryAgent;
141
+ /** Parse the canonical extracted-memory shape stored across task retries. */
142
+ export declare function parseExtractedMemory(memory: unknown): ExtractedMemory;
140
143
  /** Parse the structured decision returned by the memory agent. */
141
144
  export declare function parseMemoryReview(result: unknown): MemoryReview;
142
145
  /** Parse the structured input sent to the memory agent. */
@@ -53,19 +53,19 @@ export declare const juniorMemoryMemories: import("drizzle-orm/pg-core").PgTable
53
53
  identity: undefined;
54
54
  generated: undefined;
55
55
  }, {}, {}>;
56
- type: import("drizzle-orm/pg-core").PgColumn<{
56
+ kind: import("drizzle-orm/pg-core").PgColumn<{
57
57
  name: "type";
58
58
  tableName: "junior_memory_memories";
59
59
  dataType: "string";
60
60
  columnType: "PgText";
61
- data: "preference" | "identity" | "relationship" | "knowledge" | "context" | "event" | "task" | "observation";
61
+ data: "preference" | "procedure" | "knowledge";
62
62
  driverParam: string;
63
63
  notNull: true;
64
64
  hasDefault: false;
65
65
  isPrimaryKey: false;
66
66
  isAutoincrement: false;
67
67
  hasRuntimeDefault: false;
68
- enumValues: ["preference", "identity", "relationship", "knowledge", "context", "event", "task", "observation"];
68
+ enumValues: ["preference", "procedure", "knowledge"];
69
69
  baseColumn: never;
70
70
  identity: undefined;
71
71
  generated: undefined;
package/dist/index.d.ts CHANGED
@@ -2,4 +2,5 @@ export { createMemoryPlugin, memoryPlugin } from "./plugin";
2
2
  export type { MemoryPluginOptions } from "./plugin";
3
3
  export { createMemoryStore } from "./store";
4
4
  export type { ArchiveMemoryInput, CreateMemoryInput, CreateMemoryResult, ListMemoriesInput, MemoryDb, MemoryEmbeddingProvider, MemoryRecord, MemoryStore, MemoryStoreOptions, SearchMemoriesInput, } from "./store";
5
- export type { MemoryRuntimeContext } from "./types";
5
+ export { MEMORY_KINDS } from "./types";
6
+ export type { MemoryKind, MemoryRuntimeContext } from "./types";
package/dist/index.js CHANGED
@@ -12,16 +12,7 @@ import {
12
12
  slackSourceSchema
13
13
  } from "@sentry/junior-plugin-api";
14
14
  import { z } from "zod";
15
- var MEMORY_TYPES = [
16
- "preference",
17
- "identity",
18
- "relationship",
19
- "knowledge",
20
- "context",
21
- "event",
22
- "task",
23
- "observation"
24
- ];
15
+ var MEMORY_KINDS = ["preference", "procedure", "knowledge"];
25
16
  var MEMORY_SCOPES = ["personal", "conversation"];
26
17
  var MEMORY_SUBJECT_TYPES = [
27
18
  "user",
@@ -51,8 +42,7 @@ var memoryRuntimeContextSchema = z.union([
51
42
  ]);
52
43
 
53
44
  // src/agent.ts
54
- var memoryTargetSchema = z2.enum(["requester", "conversation"]);
55
- var memoryKindSchema = z2.enum(["preference", "procedure", "fact"]);
45
+ var memoryKindSchema = z2.enum(MEMORY_KINDS);
56
46
  var memoryRejectReasonSchema = z2.enum([
57
47
  "not_public_shareable",
58
48
  "secret_or_credential",
@@ -100,7 +90,7 @@ var expiresAtMsSchema = z2.number().finite().nullable().describe(
100
90
  var memoryReviewDecisionSchema = z2.discriminatedUnion("decision", [
101
91
  z2.object({
102
92
  decision: z2.literal("store"),
103
- target: memoryTargetSchema,
93
+ kind: memoryKindSchema,
104
94
  content: z2.string().min(1),
105
95
  expiresAtMs: z2.number().finite().optional()
106
96
  }).strict(),
@@ -113,7 +103,7 @@ var memoryReviewResponseSchema = z2.discriminatedUnion("decision", [
113
103
  z2.object({
114
104
  decision: z2.literal("store"),
115
105
  kind: memoryKindSchema.describe(
116
- "Use preference only for requester-owned personal preferences, opinions, habits, or workflows. Use procedure for reusable task or process instructions. Use fact for shared project, channel, operational, or runbook knowledge."
106
+ "Use preference only for requester-owned personal preferences, opinions, habits, or workflows. Use procedure for reusable task or process instructions. Use knowledge for shared project, channel, operational, or runbook facts."
117
107
  ),
118
108
  canonicalFact: z2.string().min(1).describe(
119
109
  "Stored memory text. It must be self-contained and must not include requester names, requester/user labels, source labels, or first- or second-person wording."
@@ -127,13 +117,18 @@ var memoryReviewResponseSchema = z2.discriminatedUnion("decision", [
127
117
  ]);
128
118
  var extractedMemorySchema = z2.object({
129
119
  kind: memoryKindSchema.describe(
130
- "Use preference only for requester-owned personal preferences, opinions, habits, or workflows. Use procedure for reusable task or process instructions. Use fact for shared project, channel, operational, or runbook knowledge."
120
+ "Use preference only for requester-owned personal preferences, opinions, habits, or workflows. Use procedure for reusable task or process instructions. Use knowledge for shared project, channel, operational, or runbook facts."
131
121
  ),
132
122
  canonicalFact: z2.string().min(1).describe(
133
123
  "Stored memory text as one self-contained fact. It must not include requester names, requester/user labels, source labels, or first- or second-person wording."
134
124
  ),
135
125
  expiresAtMs: expiresAtMsSchema
136
126
  }).strict();
127
+ var extractedMemoryResultSchema = z2.object({
128
+ content: z2.string().min(1),
129
+ expiresAtMs: expiresAtMsSchema,
130
+ kind: memoryKindSchema
131
+ }).strict();
137
132
  var extractMemoriesResponseSchema = z2.object({
138
133
  memories: z2.array(extractedMemorySchema).max(5).describe(
139
134
  "Accepted public/shareable durable memories from the completed run. Return one object per distinct source assertion and classify it with kind."
@@ -162,12 +157,6 @@ var CANONICAL_CONTENT_RULES = [
162
157
  "- Drop perspective/provenance markers while preserving useful context.",
163
158
  "- Remove requester names, display names, requester/user labels, first- or second-person wording, thread labels, channel labels, and source labels."
164
159
  ];
165
- function targetForKind(kind) {
166
- if (kind === "preference") {
167
- return "requester";
168
- }
169
- return "conversation";
170
- }
171
160
  function escapeXml(value) {
172
161
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
173
162
  }
@@ -213,7 +202,7 @@ function memoryKindsContext() {
213
202
  "<memory-kinds>",
214
203
  "- preference: a durable first-person personal preference, opinion, habit, or workflow owned by the current requester. Stored as requester memory.",
215
204
  "- procedure: reusable instructions for how a task, lookup, investigation, process, triage flow, or runbook should be done. Store the method, source-of-truth, prerequisite, or decision path when it took effort to discover. Stored as conversation memory.",
216
- "- fact: stable shared project, channel, operational, or runbook knowledge that is not a personal requester preference. Direct answers to user inquiries qualify only when they are durable beyond this run. Stored as conversation memory.",
205
+ "- knowledge: stable shared project, channel, operational, or runbook fact that is not a personal requester preference. Direct answers to user inquiries qualify only when they are durable beyond this run. Stored as conversation memory.",
217
206
  "</memory-kinds>"
218
207
  ].join("\n");
219
208
  }
@@ -232,14 +221,14 @@ function reviewPrompt(request) {
232
221
  "",
233
222
  "<rules>",
234
223
  "- Return store only when the candidate is public/shareable, durable, and self-contained.",
235
- "- First classify the memory kind: preference, procedure, or fact.",
224
+ "- First classify the memory kind: preference, procedure, or knowledge.",
236
225
  "- Use kind=preference only for first-person facts authored by the current requester about their own preference, opinion, habit, identity, or workflow.",
237
226
  "- Reject named third-person personal facts such as another person's preference, opinion, habit, identity, relationship, or workflow. Do not assume a named person is the current requester.",
238
227
  "- Use kind=procedure for reusable task/process/runbook instructions.",
239
- "- Use kind=fact for shared project, channel, operational, or runbook knowledge.",
228
+ "- Use kind=knowledge for shared project, channel, operational, or runbook facts.",
240
229
  "- When current-user-message contains an explicit memory request with a concrete fact or procedure, extract from current-user-message even if the candidate is vague, incomplete, or phrased as an instruction.",
241
230
  "- A candidate may be badly phrased by an outer assistant or extraction pass. When current-user-message contains the requester's own first-person memory fact, treat that as requester-authored source evidence and canonicalize the fact instead of rejecting for third-person wording.",
242
- "- When candidate wording personalizes a shared task, process, runbook, project, channel, or operational fact, use current-user-message to recover the shared fact and classify it as procedure or fact.",
231
+ "- When candidate wording personalizes a shared task, process, runbook, project, channel, or operational fact, use current-user-message to recover the shared fact and classify it as procedure or knowledge.",
243
232
  "- Explicit procedure requests are valid when the source text contains both task context and action. Canonicalize them as shared procedure facts instead of rejecting them as vague.",
244
233
  "- Store content as person-less, source-less canonical knowledge. Ownership and source live in structured metadata, not prose.",
245
234
  "- For requester memories, omit the subject and write the content as a stable fact such as 'Prefers X', 'Uses Y', or 'Thinks Z'.",
@@ -300,7 +289,7 @@ function sessionExtractionPrompt(request) {
300
289
  "- Do not store the fact that the user asked for advice, search, recall, planning, listing, inspection, or removal. Store only stable knowledge discovered in response, such as a reusable method or source-of-truth.",
301
290
  "- A user question asking how, what, where, or whether to do something is not source evidence for the answer. Store the answer only when supported by a user-authored factual statement or a tool result.",
302
291
  "- Set kind=procedure for reusable task/process/runbook instructions.",
303
- "- Set kind=fact for shared team, project, channel, runbook, or operational knowledge.",
292
+ "- Set kind=knowledge for shared team, project, channel, runbook, or operational facts.",
304
293
  "- Set kind=preference only for clear durable first-person facts authored by the current requester about their own preference, opinion, habit, identity, or workflow.",
305
294
  "- Reject named third-person personal facts such as another person's preference, opinion, habit, identity, relationship, or workflow. Do not assume a named person is the current requester.",
306
295
  "- User-authored task instructions are procedures, not preferences, unless they explicitly describe the requester's personal preference or habit.",
@@ -344,7 +333,7 @@ function memoryReviewFromResponse(response) {
344
333
  if (response.decision === "store") {
345
334
  return parseMemoryReview({
346
335
  decision: "store",
347
- target: targetForKind(response.kind),
336
+ kind: response.kind,
348
337
  content: response.canonicalFact,
349
338
  ...response.expiresAtMs !== null ? { expiresAtMs: response.expiresAtMs } : {}
350
339
  });
@@ -355,13 +344,16 @@ function memoryReviewFromResponse(response) {
355
344
  });
356
345
  }
357
346
  function extractedMemoriesFromResponse(response) {
358
- const toMemory = (memory) => ({
347
+ const toMemory = (memory) => parseExtractedMemory({
359
348
  content: memory.canonicalFact,
360
349
  expiresAtMs: memory.expiresAtMs,
361
- target: targetForKind(memory.kind)
350
+ kind: memory.kind
362
351
  });
363
352
  return response.memories.map(toMemory);
364
353
  }
354
+ function parseExtractedMemory(memory) {
355
+ return extractedMemoryResultSchema.parse(memory);
356
+ }
365
357
  function parseMemoryReview(result) {
366
358
  return memoryReviewDecisionSchema.parse(result);
367
359
  }
@@ -391,7 +383,7 @@ var juniorMemoryMemories = pgTable(
391
383
  id: text("id").primaryKey(),
392
384
  scope: text("scope", { enum: MEMORY_SCOPES }).notNull(),
393
385
  scopeKey: text("scope_key").notNull(),
394
- type: text("type", { enum: MEMORY_TYPES }).notNull(),
386
+ kind: text("type", { enum: MEMORY_KINDS }).notNull(),
395
387
  subjectType: text("subject_type", { enum: MEMORY_SUBJECT_TYPES }).notNull(),
396
388
  subjectKey: text("subject_key"),
397
389
  content: text("content").notNull(),
@@ -423,16 +415,11 @@ var juniorMemoryMemories = pgTable(
423
415
  sql`${table.scope} IN ('personal', 'conversation')`
424
416
  ),
425
417
  check(
426
- "junior_memory_memories_type_check",
427
- sql`${table.type} IN (
418
+ "junior_memory_memories_kind_check",
419
+ sql`${table.kind} IN (
428
420
  'preference',
429
- 'identity',
430
- 'relationship',
431
- 'knowledge',
432
- 'context',
433
- 'event',
434
- 'task',
435
- 'observation'
421
+ 'procedure',
422
+ 'knowledge'
436
423
  )`
437
424
  ),
438
425
  check(
@@ -492,7 +479,7 @@ function formatMemory(row, args) {
492
479
  `scope_key=${row.scopeKey}`,
493
480
  `subject_type=${row.subjectType}`,
494
481
  ...row.subjectKey ? [`subject_key=${row.subjectKey}`] : [],
495
- `type=${row.type}`,
482
+ `kind=${row.kind}`,
496
483
  `created_at=${formatDate(row.createdAtMs)}`,
497
484
  `observed_at=${formatDate(row.observedAtMs)}`,
498
485
  `expires_at=${formatDate(row.expiresAtMs)}`,
@@ -623,8 +610,10 @@ import {
623
610
  eq as eq3,
624
611
  gt as gt2,
625
612
  ilike as ilike2,
613
+ inArray,
626
614
  isNull as isNull2,
627
615
  like,
616
+ lte,
628
617
  or as or2,
629
618
  sql as sql2
630
619
  } from "drizzle-orm";
@@ -702,6 +691,7 @@ function deriveVisibleMemoryScopes(ctx) {
702
691
  // src/store.ts
703
692
  var DEFAULT_LIST_LIMIT = 50;
704
693
  var DEFAULT_SEARCH_LIMIT = 10;
694
+ var DEFAULT_EXPIRED_ARCHIVE_LIMIT = 100;
705
695
  var VECTOR_SEARCH_OVERFETCH = 4;
706
696
  var MAX_MEMORY_CONTENT_CHARS = 4e3;
707
697
  var EMBEDDING_METRIC = "cosine";
@@ -713,7 +703,8 @@ var numberSchema = z3.number().finite();
713
703
  var createMemoryInputSchema = z3.object({
714
704
  content: memoryContentSchema,
715
705
  expiresAtMs: numberSchema.optional(),
716
- idempotencyKey: nonEmptyStringSchema2
706
+ idempotencyKey: nonEmptyStringSchema2,
707
+ kind: z3.enum(MEMORY_KINDS)
717
708
  }).strict();
718
709
  var listMemoriesInputSchema = z3.object({
719
710
  limit: numberSchema.optional()
@@ -726,6 +717,9 @@ var archiveMemoryInputSchema = z3.object({
726
717
  id: nonEmptyStringSchema2,
727
718
  reason: nonEmptyStringSchema2.optional()
728
719
  }).strict();
720
+ var archiveExpiredMemoriesInputSchema = z3.object({
721
+ limit: numberSchema.optional()
722
+ }).strict();
729
723
  var clockSchema = z3.function({ input: [], output: numberSchema }).optional();
730
724
  var memoryStoreOptionsSchema = z3.object({
731
725
  now: clockSchema
@@ -759,7 +753,7 @@ var memoryRowSchema = z3.object({
759
753
  subjectType: z3.enum(MEMORY_SUBJECT_TYPES),
760
754
  supersededAtMs: optionalNumberSchema,
761
755
  supersededById: optionalStringSchema,
762
- type: z3.enum(MEMORY_TYPES)
756
+ kind: z3.enum(MEMORY_KINDS)
763
757
  }).strict().superRefine((row, ctx) => {
764
758
  if (row.subjectType === "general") {
765
759
  if (row.subjectKey !== void 0) {
@@ -791,7 +785,7 @@ var memoryRecordSchema = z3.object({
791
785
  subjectType: z3.enum(MEMORY_SUBJECT_TYPES),
792
786
  supersededAtMs: numberSchema.optional(),
793
787
  supersededById: nonEmptyStringSchema2.optional(),
794
- type: z3.enum(MEMORY_TYPES)
788
+ kind: z3.enum(MEMORY_KINDS)
795
789
  }).strict();
796
790
  var embeddingVectorSchema = z3.array(numberSchema).length(MEMORY_EMBEDDING_DIMENSIONS);
797
791
  var embeddingResultSchema = z3.object({
@@ -829,7 +823,7 @@ function parseMemoryRow(row) {
829
823
  return memoryRecordSchema.parse({
830
824
  id: parsed.id,
831
825
  scope: parsed.scope,
832
- type: parsed.type,
826
+ kind: parsed.kind,
833
827
  subjectType: parsed.subjectType,
834
828
  content: parsed.content,
835
829
  observedAtMs: parsed.observedAtMs,
@@ -878,11 +872,53 @@ async function findByIdempotencyKey(args) {
878
872
  eq3(juniorMemoryMemories.idempotencyKey, args.idempotencyKey),
879
873
  isNull2(juniorMemoryMemories.archivedAtMs),
880
874
  isNull2(juniorMemoryMemories.supersededAtMs),
881
- isNull2(juniorMemoryMemories.supersededById)
875
+ isNull2(juniorMemoryMemories.supersededById),
876
+ or2(
877
+ isNull2(juniorMemoryMemories.expiresAtMs),
878
+ gt2(juniorMemoryMemories.expiresAtMs, args.nowMs)
879
+ )
882
880
  )
883
881
  ).limit(1);
884
882
  return rows[0] ? parseMemoryRow(rows[0]) : void 0;
885
883
  }
884
+ async function archiveExpiredMemoryBatch(args) {
885
+ const scopePredicate = visibleScopePredicate(args.scopes);
886
+ if (!scopePredicate) {
887
+ return { archivedCount: 0 };
888
+ }
889
+ const predicates = [
890
+ scopePredicate,
891
+ isNull2(juniorMemoryMemories.archivedAtMs),
892
+ isNull2(juniorMemoryMemories.supersededAtMs),
893
+ isNull2(juniorMemoryMemories.supersededById),
894
+ lte(juniorMemoryMemories.expiresAtMs, args.nowMs)
895
+ ];
896
+ if (args.idempotencyKey !== void 0) {
897
+ predicates.push(
898
+ eq3(juniorMemoryMemories.idempotencyKey, args.idempotencyKey)
899
+ );
900
+ }
901
+ const archivedIds = await args.db.transaction(async (tx) => {
902
+ const expired = await tx.select({ id: juniorMemoryMemories.id }).from(juniorMemoryMemories).where(and2(...predicates)).orderBy(
903
+ asc(juniorMemoryMemories.expiresAtMs),
904
+ asc(juniorMemoryMemories.id)
905
+ ).limit(boundedLimit(args.limit, DEFAULT_EXPIRED_ARCHIVE_LIMIT));
906
+ const ids = expired.map((row) => row.id);
907
+ if (ids.length === 0) {
908
+ return [];
909
+ }
910
+ const archived = await tx.update(juniorMemoryMemories).set({
911
+ archivedAtMs: args.nowMs,
912
+ archiveReason: "expired"
913
+ }).where(and2(inArray(juniorMemoryMemories.id, ids), ...predicates)).returning({ id: juniorMemoryMemories.id });
914
+ const idsToClean = archived.map((row) => row.id);
915
+ if (idsToClean.length > 0) {
916
+ await tx.delete(juniorMemoryEmbeddings).where(inArray(juniorMemoryEmbeddings.memoryId, idsToClean));
917
+ }
918
+ return idsToClean;
919
+ });
920
+ return { archivedCount: archivedIds.length };
921
+ }
886
922
  function searchScore(memory, terms) {
887
923
  const haystack = memory.content.toLowerCase();
888
924
  return terms.reduce(
@@ -1053,6 +1089,15 @@ function createMemoryStore(db, context, options = {}) {
1053
1089
  const parsedOptions = memoryStoreOptionsSchema.parse({ now: options.now });
1054
1090
  const embedder = options.embedder;
1055
1091
  const getNowMs = parsedOptions.now ?? Date.now;
1092
+ async function archiveExpiredVisibleMemories(input, nowMs) {
1093
+ input = archiveExpiredMemoriesInputSchema.parse(input ?? {});
1094
+ return await archiveExpiredMemoryBatch({
1095
+ db,
1096
+ limit: input.limit,
1097
+ nowMs,
1098
+ scopes: deriveVisibleMemoryScopes(runtimeContext)
1099
+ });
1100
+ }
1056
1101
  async function createScopedMemory(rawInput, scopeKind) {
1057
1102
  const input = createMemoryInputSchema.parse(rawInput);
1058
1103
  const nowMs = getNowMs();
@@ -1062,6 +1107,18 @@ function createMemoryStore(db, context, options = {}) {
1062
1107
  if (content.length > MAX_MEMORY_CONTENT_CHARS) {
1063
1108
  throw new Error("Memory content exceeds the maximum length.");
1064
1109
  }
1110
+ await archiveExpiredMemoryBatch({
1111
+ db,
1112
+ nowMs,
1113
+ scopes: [scope]
1114
+ });
1115
+ await archiveExpiredMemoryBatch({
1116
+ db,
1117
+ idempotencyKey: input.idempotencyKey,
1118
+ limit: 1,
1119
+ nowMs,
1120
+ scopes: [scope]
1121
+ });
1065
1122
  const id = randomUUID();
1066
1123
  const rows = await db.insert(juniorMemoryMemories).values({
1067
1124
  content,
@@ -1076,7 +1133,7 @@ function createMemoryStore(db, context, options = {}) {
1076
1133
  sourcePlatform: runtimeContext.source.platform,
1077
1134
  subjectKey: subject.subjectKey,
1078
1135
  subjectType: subject.subjectType,
1079
- type: "knowledge"
1136
+ kind: input.kind
1080
1137
  }).onConflictDoNothing({
1081
1138
  target: [
1082
1139
  juniorMemoryMemories.scope,
@@ -1099,6 +1156,7 @@ function createMemoryStore(db, context, options = {}) {
1099
1156
  const idempotent = await findByIdempotencyKey({
1100
1157
  db,
1101
1158
  idempotencyKey: input.idempotencyKey,
1159
+ nowMs,
1102
1160
  scope
1103
1161
  });
1104
1162
  if (!idempotent) {
@@ -1114,6 +1172,9 @@ function createMemoryStore(db, context, options = {}) {
1114
1172
  return { created: false, memory: idempotent };
1115
1173
  }
1116
1174
  return {
1175
+ async archiveExpiredMemories(input) {
1176
+ return await archiveExpiredVisibleMemories(input, getNowMs());
1177
+ },
1117
1178
  async createMemory(input) {
1118
1179
  return await createScopedMemory(input, "personal");
1119
1180
  },
@@ -1124,6 +1185,11 @@ function createMemoryStore(db, context, options = {}) {
1124
1185
  input = listMemoriesInputSchema.parse(input);
1125
1186
  const nowMs = getNowMs();
1126
1187
  const scopes = deriveVisibleMemoryScopes(runtimeContext);
1188
+ await archiveExpiredMemoryBatch({
1189
+ db,
1190
+ nowMs,
1191
+ scopes
1192
+ });
1127
1193
  return await listVisibleMemories({
1128
1194
  db,
1129
1195
  limit: input.limit,
@@ -1135,6 +1201,11 @@ function createMemoryStore(db, context, options = {}) {
1135
1201
  input = searchMemoriesInputSchema.parse(input);
1136
1202
  const nowMs = getNowMs();
1137
1203
  const scopes = deriveVisibleMemoryScopes(runtimeContext);
1204
+ await archiveExpiredMemoryBatch({
1205
+ db,
1206
+ nowMs,
1207
+ scopes
1208
+ });
1138
1209
  const limit = boundedLimit(input.limit, DEFAULT_SEARCH_LIMIT);
1139
1210
  const vectorCandidates = await searchVisibleVectorMemories({
1140
1211
  db,
@@ -1394,9 +1465,16 @@ function createInput(context, input, toolCallId) {
1394
1465
  return {
1395
1466
  content: requireMemoryContent(input.content),
1396
1467
  idempotencyKey: `tool:${sourceIdempotencyKey(context)}:${toolCallId}`,
1468
+ kind: input.kind,
1397
1469
  ...input.expiresAtMs !== void 0 ? { expiresAtMs: input.expiresAtMs } : {}
1398
1470
  };
1399
1471
  }
1472
+ function targetForKind(kind) {
1473
+ if (kind === "preference") {
1474
+ return "requester";
1475
+ }
1476
+ return "conversation";
1477
+ }
1400
1478
  function compactMemory(memory) {
1401
1479
  return {
1402
1480
  id: memory.id,
@@ -1407,7 +1485,7 @@ function compactMemory(memory) {
1407
1485
  }
1408
1486
  function createMemoryCreateTool(context) {
1409
1487
  return {
1410
- description: "Explicit memory-write tool. Use only when the latest user message directly asks Junior to remember, store, save, or forget-and-replace a public/shareable fact. Do not use for ordinary statements like 'I prefer X', 'I use Y', or 'X goes before Y' unless the user also asks you to remember/store/save it; passive memory learning handles those after the visible reply. Pass one self-contained natural-language candidate preserving the user's explicit memory intent. Do not ask the user to rephrase ordinary first-person facts, and do not rewrite them into display-name or third-person wording. Do not include secrets, private personal details, medical/legal/financial/sensitive facts, or another person's personal preference, opinion, habit, identity, relationship, workflow, or private life. Runtime context derives actor, scope, source, and subject ids; the memory agent decides the canonical stored content, subject, and target.",
1488
+ description: "Explicit memory-write tool. Use only when the latest user message directly asks Junior to remember, store, save, or forget-and-replace a public/shareable fact. Do not use for ordinary statements like 'I prefer X', 'I use Y', or 'X goes before Y' unless the user also asks you to remember/store/save it; passive memory learning handles those after the visible reply. Pass one self-contained natural-language candidate preserving the user's explicit memory intent. Do not ask the user to rephrase ordinary first-person facts, and do not rewrite them into display-name or third-person wording. Do not include secrets, private personal details, medical/legal/financial/sensitive facts, or another person's personal preference, opinion, habit, identity, relationship, workflow, or private life. Runtime context derives actor, scope, source, and subject ids; the memory agent decides canonical stored content and memory kind, then the plugin derives storage target from kind.",
1411
1489
  executionMode: "sequential",
1412
1490
  inputSchema: createMemoryInputSchema2,
1413
1491
  execute: async (input, options) => {
@@ -1455,13 +1533,14 @@ function createMemoryCreateTool(context) {
1455
1533
  context,
1456
1534
  {
1457
1535
  content: review.content,
1536
+ kind: review.kind,
1458
1537
  ...review.expiresAtMs !== void 0 ? { expiresAtMs: review.expiresAtMs } : requestedExpiresAtMs !== void 0 ? { expiresAtMs: requestedExpiresAtMs } : {}
1459
1538
  },
1460
1539
  toolCallId
1461
1540
  );
1462
1541
  const result = await (async () => {
1463
1542
  try {
1464
- if (review.target === "conversation") {
1543
+ if (targetForKind(review.kind) === "conversation") {
1465
1544
  return await store.createConversationMemory(memoryInput);
1466
1545
  }
1467
1546
  return await store.createMemory(memoryInput);
@@ -1564,16 +1643,23 @@ var extractedMemoryCacheSchema = z4.array(
1564
1643
  z4.object({
1565
1644
  content: z4.string().min(1),
1566
1645
  expiresAtMs: z4.number().finite().nullable(),
1567
- target: z4.enum(["requester", "conversation"])
1568
- }).strict()
1646
+ kind: z4.enum(MEMORY_KINDS)
1647
+ }).strict().transform(parseExtractedMemory)
1569
1648
  );
1649
+ function targetForKind2(kind) {
1650
+ if (kind === "preference") {
1651
+ return "requester";
1652
+ }
1653
+ return "conversation";
1654
+ }
1570
1655
  function memoryIdempotencySuffix(memory) {
1571
- return createHash2("sha256").update(memory.target).update("\0").update(memory.content).update("\0").update(memory.expiresAtMs === null ? "never" : String(memory.expiresAtMs)).digest("hex").slice(0, 32);
1656
+ return createHash2("sha256").update(targetForKind2(memory.kind)).update("\0").update(memory.kind).update("\0").update(memory.content).update("\0").update(memory.expiresAtMs === null ? "never" : String(memory.expiresAtMs)).digest("hex").slice(0, 32);
1572
1657
  }
1573
1658
  function passiveInput(sessionId, memory, sourceKey2) {
1574
1659
  return {
1575
1660
  content: memory.content,
1576
1661
  idempotencyKey: `session:${sourceKey2}:${sessionId}:${memoryIdempotencySuffix(memory)}`,
1662
+ kind: memory.kind,
1577
1663
  ...memory.expiresAtMs !== null ? { expiresAtMs: memory.expiresAtMs } : {}
1578
1664
  };
1579
1665
  }
@@ -1616,6 +1702,7 @@ async function processMemorySession(context) {
1616
1702
  const store = createMemoryStore(context.db, runtimeContext, {
1617
1703
  embedder: context.embedder
1618
1704
  });
1705
+ await store.archiveExpiredMemories();
1619
1706
  const memories = await getTaskMemories(context, async () => {
1620
1707
  const existingMemories = await store.searchMemories({
1621
1708
  limit: 10,
@@ -1635,7 +1722,7 @@ async function processMemorySession(context) {
1635
1722
  }
1636
1723
  for (const memory of memories) {
1637
1724
  const input = passiveInput(run.runId, memory, sourceKey2);
1638
- if (memory.target === "conversation") {
1725
+ if (targetForKind2(memory.kind) === "conversation") {
1639
1726
  await store.createConversationMemory(input);
1640
1727
  continue;
1641
1728
  }
@@ -1770,6 +1857,7 @@ function createMemoryPlugin(options = {}) {
1770
1857
  }
1771
1858
  var memoryPlugin = createMemoryPlugin();
1772
1859
  export {
1860
+ MEMORY_KINDS,
1773
1861
  createMemoryPlugin,
1774
1862
  createMemoryStore,
1775
1863
  memoryPlugin