@sentry/junior-memory 0.122.1 → 0.124.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/events.d.ts +19 -0
- package/dist/index.js +130 -39
- package/dist/index.js.map +1 -1
- package/dist/store.d.ts +4 -0
- package/package.json +2 -2
- package/src/events.ts +59 -0
- package/src/plugin.ts +2 -0
- package/src/process-session.ts +25 -2
- package/src/store.ts +34 -15
- package/src/tools.ts +24 -2
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MemoryRecord } from "./store";
|
|
2
|
+
/** Durable transcript event emitted after background memory capture. */
|
|
3
|
+
export declare const memoriesCapturedEvent: import("@sentry/junior-plugin-api").DefinedConversationEvent<{
|
|
4
|
+
memories: {
|
|
5
|
+
content: string;
|
|
6
|
+
id: string;
|
|
7
|
+
kind: "preference" | "procedure" | "knowledge";
|
|
8
|
+
observedAtMs: number;
|
|
9
|
+
scope: "personal" | "conversation";
|
|
10
|
+
}[];
|
|
11
|
+
}>;
|
|
12
|
+
/** Select the stable, safe memory fields retained in conversation history. */
|
|
13
|
+
export declare function capturedMemory(memory: MemoryRecord): {
|
|
14
|
+
content: string;
|
|
15
|
+
id: string;
|
|
16
|
+
kind: "preference" | "procedure" | "knowledge";
|
|
17
|
+
observedAtMs: number;
|
|
18
|
+
scope: "personal" | "conversation";
|
|
19
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -527,7 +527,7 @@ async function findByIdempotencyKey(args) {
|
|
|
527
527
|
)
|
|
528
528
|
).limit(1);
|
|
529
529
|
if (activeRows[0]) {
|
|
530
|
-
return parseMemoryRow(activeRows[0]);
|
|
530
|
+
return { memory: parseMemoryRow(activeRows[0]), outcome: "created" };
|
|
531
531
|
}
|
|
532
532
|
const aliasRows = await args.db.select({ supersededById: juniorMemoryMemories.supersededById }).from(juniorMemoryMemories).where(
|
|
533
533
|
and(
|
|
@@ -565,7 +565,7 @@ async function findByIdempotencyKey(args) {
|
|
|
565
565
|
)
|
|
566
566
|
).limit(1);
|
|
567
567
|
if (rows[0]) {
|
|
568
|
-
return parseMemoryRow(rows[0]);
|
|
568
|
+
return { memory: parseMemoryRow(rows[0]), outcome: "duplicate" };
|
|
569
569
|
}
|
|
570
570
|
}
|
|
571
571
|
return void 0;
|
|
@@ -1023,13 +1023,13 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1023
1023
|
});
|
|
1024
1024
|
if (idempotent2) {
|
|
1025
1025
|
await storeEmbedding({
|
|
1026
|
-
content: idempotent2.content,
|
|
1026
|
+
content: idempotent2.memory.content,
|
|
1027
1027
|
db,
|
|
1028
1028
|
embedder,
|
|
1029
|
-
memoryId: idempotent2.id,
|
|
1029
|
+
memoryId: idempotent2.memory.id,
|
|
1030
1030
|
nowMs
|
|
1031
1031
|
});
|
|
1032
|
-
return { created: false, memory: idempotent2 };
|
|
1032
|
+
return idempotent2.outcome === "created" ? { created: false, idempotent: true, memory: idempotent2.memory } : { created: false, memory: idempotent2.memory };
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
const exactDuplicate = await findExactDuplicateMemory({
|
|
@@ -1088,7 +1088,7 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1088
1088
|
}
|
|
1089
1089
|
}
|
|
1090
1090
|
const id = randomUUID();
|
|
1091
|
-
const
|
|
1091
|
+
const write = await db.transaction(async (tx) => {
|
|
1092
1092
|
const inserted = await tx.insert(juniorMemoryMemories).values({
|
|
1093
1093
|
content,
|
|
1094
1094
|
createdAtMs: nowMs,
|
|
@@ -1113,7 +1113,7 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1113
1113
|
}).returning();
|
|
1114
1114
|
const insertedMemory = inserted[0];
|
|
1115
1115
|
if (!insertedMemory || supersededIds.length === 0) {
|
|
1116
|
-
return inserted;
|
|
1116
|
+
return { inserted, supersededIds: [] };
|
|
1117
1117
|
}
|
|
1118
1118
|
const superseded = await tx.update(juniorMemoryMemories).set({
|
|
1119
1119
|
supersededAtMs: nowMs,
|
|
@@ -1133,10 +1133,10 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1133
1133
|
if (idsToClean.length > 0) {
|
|
1134
1134
|
await tx.delete(juniorMemoryEmbeddings).where(inArray(juniorMemoryEmbeddings.memoryId, idsToClean));
|
|
1135
1135
|
}
|
|
1136
|
-
return inserted;
|
|
1136
|
+
return { inserted, supersededIds: idsToClean };
|
|
1137
1137
|
});
|
|
1138
|
-
if (
|
|
1139
|
-
const memory = parseMemoryRow(
|
|
1138
|
+
if (write.inserted[0]) {
|
|
1139
|
+
const memory = parseMemoryRow(write.inserted[0]);
|
|
1140
1140
|
await storeEmbedding({
|
|
1141
1141
|
content: memory.content,
|
|
1142
1142
|
db,
|
|
@@ -1145,7 +1145,11 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1145
1145
|
memoryId: memory.id,
|
|
1146
1146
|
nowMs
|
|
1147
1147
|
});
|
|
1148
|
-
return {
|
|
1148
|
+
return {
|
|
1149
|
+
created: true,
|
|
1150
|
+
memory,
|
|
1151
|
+
...write.supersededIds.length > 0 ? { supersededIds: write.supersededIds } : {}
|
|
1152
|
+
};
|
|
1149
1153
|
}
|
|
1150
1154
|
const idempotent = await findByIdempotencyKey({
|
|
1151
1155
|
db,
|
|
@@ -1157,13 +1161,13 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1157
1161
|
throw new Error("Memory idempotency conflict did not resolve.");
|
|
1158
1162
|
}
|
|
1159
1163
|
await storeEmbedding({
|
|
1160
|
-
content: idempotent.content,
|
|
1164
|
+
content: idempotent.memory.content,
|
|
1161
1165
|
db,
|
|
1162
1166
|
embedder,
|
|
1163
|
-
memoryId: idempotent.id,
|
|
1167
|
+
memoryId: idempotent.memory.id,
|
|
1164
1168
|
nowMs
|
|
1165
1169
|
});
|
|
1166
|
-
return { created: false, memory: idempotent };
|
|
1170
|
+
return idempotent.outcome === "created" ? { created: false, idempotent: true, memory: idempotent.memory } : { created: false, memory: idempotent.memory };
|
|
1167
1171
|
}
|
|
1168
1172
|
async function retrieveVisibleMemories(rawInput, vectorMaxDistance) {
|
|
1169
1173
|
const input = searchMemoriesInputSchema.parse(rawInput);
|
|
@@ -2438,6 +2442,12 @@ function memoryToolResult(target, data) {
|
|
|
2438
2442
|
}
|
|
2439
2443
|
function createMemoryCreateTool(context) {
|
|
2440
2444
|
return definePluginTool({
|
|
2445
|
+
annotations: {
|
|
2446
|
+
destructiveHint: false,
|
|
2447
|
+
idempotentHint: true,
|
|
2448
|
+
openWorldHint: false,
|
|
2449
|
+
readOnlyHint: false
|
|
2450
|
+
},
|
|
2441
2451
|
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.",
|
|
2442
2452
|
executionMode: "sequential",
|
|
2443
2453
|
inputSchema: createMemoryInputSchema2,
|
|
@@ -2510,6 +2520,12 @@ function createMemoryCreateTool(context) {
|
|
|
2510
2520
|
}
|
|
2511
2521
|
function createMemoryRemoveTool(context) {
|
|
2512
2522
|
return definePluginTool({
|
|
2523
|
+
annotations: {
|
|
2524
|
+
destructiveHint: true,
|
|
2525
|
+
idempotentHint: true,
|
|
2526
|
+
openWorldHint: false,
|
|
2527
|
+
readOnlyHint: false
|
|
2528
|
+
},
|
|
2513
2529
|
description: "Forget one memory visible in the active context. Use only ids or short id prefixes returned by listMemories or searchMemories. Never remove memories by hidden actor, Slack, scope, or subject identifiers.",
|
|
2514
2530
|
executionMode: "sequential",
|
|
2515
2531
|
inputSchema: removeMemoryInputSchema,
|
|
@@ -2535,7 +2551,12 @@ function createMemoryRemoveTool(context) {
|
|
|
2535
2551
|
function createMemoryListTool(context) {
|
|
2536
2552
|
return definePluginTool({
|
|
2537
2553
|
description: "List active memories visible in the current context. Use when the user asks what Junior remembers or when memory ids are needed before removing a memory.",
|
|
2538
|
-
annotations: {
|
|
2554
|
+
annotations: {
|
|
2555
|
+
destructiveHint: false,
|
|
2556
|
+
idempotentHint: true,
|
|
2557
|
+
openWorldHint: false,
|
|
2558
|
+
readOnlyHint: true
|
|
2559
|
+
},
|
|
2539
2560
|
inputSchema: listMemoriesInputSchema2,
|
|
2540
2561
|
outputSchema: memoryManyOutputSchema,
|
|
2541
2562
|
execute: async (input) => {
|
|
@@ -2552,7 +2573,12 @@ function createMemoryListTool(context) {
|
|
|
2552
2573
|
function createMemorySearchTool(context) {
|
|
2553
2574
|
return definePluginTool({
|
|
2554
2575
|
description: "Search active memories visible in the current context. Use when the model needs targeted memory recall. The tool searches only the current actor and active conversation scopes.",
|
|
2555
|
-
annotations: {
|
|
2576
|
+
annotations: {
|
|
2577
|
+
destructiveHint: false,
|
|
2578
|
+
idempotentHint: true,
|
|
2579
|
+
openWorldHint: false,
|
|
2580
|
+
readOnlyHint: true
|
|
2581
|
+
},
|
|
2556
2582
|
inputSchema: searchMemoriesInputSchema2,
|
|
2557
2583
|
outputSchema: memoryManyOutputSchema,
|
|
2558
2584
|
execute: async (input) => {
|
|
@@ -2577,7 +2603,54 @@ import {
|
|
|
2577
2603
|
getSourceKey as getSourceKey2,
|
|
2578
2604
|
isPrivateSource as isPrivateSource2
|
|
2579
2605
|
} from "@sentry/junior-plugin-api";
|
|
2606
|
+
import { z as z9 } from "zod";
|
|
2607
|
+
|
|
2608
|
+
// src/events.ts
|
|
2609
|
+
import { defineConversationEvent } from "@sentry/junior-plugin-api";
|
|
2580
2610
|
import { z as z8 } from "zod";
|
|
2611
|
+
var MAX_EVENT_PREVIEW_CHARS = 500;
|
|
2612
|
+
var capturedMemorySchema = z8.object({
|
|
2613
|
+
content: z8.string().min(1),
|
|
2614
|
+
id: z8.string().min(1),
|
|
2615
|
+
kind: z8.enum(MEMORY_KINDS),
|
|
2616
|
+
observedAtMs: z8.number().finite(),
|
|
2617
|
+
scope: z8.enum(MEMORY_SCOPES)
|
|
2618
|
+
}).strict();
|
|
2619
|
+
function eventPreview(content) {
|
|
2620
|
+
const trimmed = content.trim();
|
|
2621
|
+
if (trimmed.length <= MAX_EVENT_PREVIEW_CHARS) return trimmed;
|
|
2622
|
+
return `${trimmed.slice(0, MAX_EVENT_PREVIEW_CHARS - 3).trimEnd()}...`;
|
|
2623
|
+
}
|
|
2624
|
+
var memoriesCapturedEvent = defineConversationEvent({
|
|
2625
|
+
name: "memories_captured",
|
|
2626
|
+
version: 1,
|
|
2627
|
+
schema: z8.object({
|
|
2628
|
+
memories: z8.array(capturedMemorySchema).min(1).max(100)
|
|
2629
|
+
}).strict(),
|
|
2630
|
+
renderEvent(event) {
|
|
2631
|
+
const count = event.memories.length;
|
|
2632
|
+
return {
|
|
2633
|
+
icon: "brain",
|
|
2634
|
+
title: count === 1 ? "Memory captured" : "Memories captured",
|
|
2635
|
+
preview: count === 1 ? eventPreview(event.memories[0].content) : `${count} memories`,
|
|
2636
|
+
details: event.memories.map((memory) => ({
|
|
2637
|
+
title: memory.content,
|
|
2638
|
+
metadata: [memory.kind, memory.scope]
|
|
2639
|
+
}))
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
});
|
|
2643
|
+
function capturedMemory(memory) {
|
|
2644
|
+
return {
|
|
2645
|
+
content: memory.content,
|
|
2646
|
+
id: memory.id,
|
|
2647
|
+
kind: memory.kind,
|
|
2648
|
+
observedAtMs: memory.observedAtMs,
|
|
2649
|
+
scope: memory.scope
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
// src/process-session.ts
|
|
2581
2654
|
var MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
2582
2655
|
"createMemory",
|
|
2583
2656
|
"listMemories",
|
|
@@ -2585,14 +2658,25 @@ var MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
2585
2658
|
"searchMemories"
|
|
2586
2659
|
]);
|
|
2587
2660
|
var MEMORY_TASK_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2588
|
-
var extractedMemoryCacheSchema =
|
|
2589
|
-
|
|
2590
|
-
content:
|
|
2591
|
-
expiresAtMs:
|
|
2592
|
-
kind:
|
|
2593
|
-
evidenceMessageIndices:
|
|
2661
|
+
var extractedMemoryCacheSchema = z9.array(
|
|
2662
|
+
z9.object({
|
|
2663
|
+
content: z9.string().min(1),
|
|
2664
|
+
expiresAtMs: z9.number().finite().nullable(),
|
|
2665
|
+
kind: z9.enum(MEMORY_KINDS),
|
|
2666
|
+
evidenceMessageIndices: z9.array(z9.number().int().nonnegative()).min(1).max(10)
|
|
2594
2667
|
}).strict().transform(parseExtractedMemory)
|
|
2595
2668
|
);
|
|
2669
|
+
function recordCapturedMemory(captured, result) {
|
|
2670
|
+
const supersededIds = new Set(result.supersededIds ?? []);
|
|
2671
|
+
for (let index2 = captured.length - 1; index2 >= 0; index2 -= 1) {
|
|
2672
|
+
if (supersededIds.has(captured[index2].id)) {
|
|
2673
|
+
captured.splice(index2, 1);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
if (result.created || result.idempotent) {
|
|
2677
|
+
captured.push(capturedMemory(result.memory));
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2596
2680
|
function isRunActorInstruction(entry) {
|
|
2597
2681
|
return entry.type === "message" && entry.role === "user" && entry.provenance?.authority === "instruction" && entry.isRunActor === true;
|
|
2598
2682
|
}
|
|
@@ -2711,6 +2795,7 @@ async function processMemorySession(context) {
|
|
|
2711
2795
|
if (memories.length === 0) {
|
|
2712
2796
|
return;
|
|
2713
2797
|
}
|
|
2798
|
+
const captured = [];
|
|
2714
2799
|
for (const memory of memories) {
|
|
2715
2800
|
const target = routeExtractedMemory(memory, transcript, run);
|
|
2716
2801
|
if (target === "drop") {
|
|
@@ -2718,10 +2803,15 @@ async function processMemorySession(context) {
|
|
|
2718
2803
|
}
|
|
2719
2804
|
const input = passiveInput(run.runId, memory, sourceKey2, target);
|
|
2720
2805
|
if (target === "conversation") {
|
|
2721
|
-
await store.createConversationMemory(input);
|
|
2806
|
+
const result2 = await store.createConversationMemory(input);
|
|
2807
|
+
recordCapturedMemory(captured, result2);
|
|
2722
2808
|
continue;
|
|
2723
2809
|
}
|
|
2724
|
-
await store.createMemory(input);
|
|
2810
|
+
const result = await store.createMemory(input);
|
|
2811
|
+
recordCapturedMemory(captured, result);
|
|
2812
|
+
}
|
|
2813
|
+
if (captured.length > 0) {
|
|
2814
|
+
await context.events.emit(memoriesCapturedEvent({ memories: captured }));
|
|
2725
2815
|
}
|
|
2726
2816
|
}
|
|
2727
2817
|
|
|
@@ -2729,7 +2819,7 @@ async function processMemorySession(context) {
|
|
|
2729
2819
|
import {
|
|
2730
2820
|
definePromptContext
|
|
2731
2821
|
} from "@sentry/junior-plugin-api";
|
|
2732
|
-
import { z as
|
|
2822
|
+
import { z as z10 } from "zod";
|
|
2733
2823
|
var DEFAULT_RECALL_LIMIT = 5;
|
|
2734
2824
|
var RECALL_CANDIDATE_LIMIT = 20;
|
|
2735
2825
|
var MAX_PROMPT_CHARS = 4e3;
|
|
@@ -2744,15 +2834,15 @@ function trimContent(content, maxLength) {
|
|
|
2744
2834
|
function formatObservedDate(observedAtMs) {
|
|
2745
2835
|
return new Date(observedAtMs).toISOString().slice(0, 10);
|
|
2746
2836
|
}
|
|
2747
|
-
var recalledMemorySchema =
|
|
2748
|
-
id:
|
|
2749
|
-
content:
|
|
2750
|
-
observedAtMs:
|
|
2751
|
-
scope:
|
|
2752
|
-
kind:
|
|
2837
|
+
var recalledMemorySchema = z10.object({
|
|
2838
|
+
id: z10.string().min(1),
|
|
2839
|
+
content: z10.string().min(1).max(MAX_MEMORY_LINE_CHARS),
|
|
2840
|
+
observedAtMs: z10.number().finite(),
|
|
2841
|
+
scope: z10.enum(["personal", "conversation"]),
|
|
2842
|
+
kind: z10.enum(["preference", "procedure", "knowledge"])
|
|
2753
2843
|
}).strict();
|
|
2754
|
-
var memoryRecallContextSchema =
|
|
2755
|
-
memories:
|
|
2844
|
+
var memoryRecallContextSchema = z10.object({
|
|
2845
|
+
memories: z10.array(recalledMemorySchema).min(1).max(DEFAULT_RECALL_LIMIT)
|
|
2756
2846
|
}).strict();
|
|
2757
2847
|
function selectPromptMemories(memories) {
|
|
2758
2848
|
const header = "Relevant memories for this request:";
|
|
@@ -2834,13 +2924,13 @@ async function createMemoryPromptContributions(context) {
|
|
|
2834
2924
|
|
|
2835
2925
|
// src/operational-report.ts
|
|
2836
2926
|
import { and as and4, eq as eq5, gt as gt4, isNull as isNull3, or as or4, sql as sql4 } from "drizzle-orm";
|
|
2837
|
-
import { z as
|
|
2927
|
+
import { z as z11 } from "zod";
|
|
2838
2928
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
2839
2929
|
var WINDOWS = [7, 30, 90];
|
|
2840
|
-
var memoryDaySchema =
|
|
2841
|
-
conversation:
|
|
2842
|
-
date:
|
|
2843
|
-
personal:
|
|
2930
|
+
var memoryDaySchema = z11.object({
|
|
2931
|
+
conversation: z11.number().int().nonnegative(),
|
|
2932
|
+
date: z11.string().date(),
|
|
2933
|
+
personal: z11.number().int().nonnegative()
|
|
2844
2934
|
}).strict();
|
|
2845
2935
|
function queryRows(result) {
|
|
2846
2936
|
if (typeof result !== "object" || result === null || !("rows" in result) || !Array.isArray(result.rows)) {
|
|
@@ -2893,7 +2983,7 @@ async function aggregateMemoryDays(args) {
|
|
|
2893
2983
|
LEFT JOIN daily ON daily.day = days.day
|
|
2894
2984
|
ORDER BY days.day
|
|
2895
2985
|
`);
|
|
2896
|
-
return
|
|
2986
|
+
return z11.array(memoryDaySchema).parse(queryRows(result));
|
|
2897
2987
|
}
|
|
2898
2988
|
function formatCount(value) {
|
|
2899
2989
|
return new Intl.NumberFormat("en-US").format(value);
|
|
@@ -3092,6 +3182,7 @@ function memoryPlugin(options = {}) {
|
|
|
3092
3182
|
},
|
|
3093
3183
|
model: modelId ? { structuredModelId: modelId } : { structuredModel: "default" },
|
|
3094
3184
|
packageName: "@sentry/junior-memory",
|
|
3185
|
+
conversationEvents: [memoriesCapturedEvent],
|
|
3095
3186
|
cli: {
|
|
3096
3187
|
commands: [createMemoryCliCommand()]
|
|
3097
3188
|
},
|