@sentry/junior-memory 0.116.1 → 0.118.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 +2 -0
- package/dist/index.js +161 -37
- package/dist/index.js.map +1 -1
- package/dist/recall.d.ts +26 -3
- package/dist/store.d.ts +2 -0
- package/dist/user-pages.d.ts +4 -0
- package/package.json +2 -2
- package/src/plugin.ts +4 -2
- package/src/recall.ts +72 -17
- package/src/store.ts +19 -0
- package/src/user-pages.ts +94 -0
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ exported types, tools, and tests are authoritative.
|
|
|
13
13
|
learning.
|
|
14
14
|
- The `memory` CLI namespace provides explicit administrative search and
|
|
15
15
|
inspection.
|
|
16
|
+
- The dashboard exposes a read-only **Memories** user page for personal
|
|
17
|
+
memories owned by actors linked to the signed-in user.
|
|
16
18
|
|
|
17
19
|
## Scope And Visibility
|
|
18
20
|
|
package/dist/index.js
CHANGED
|
@@ -949,7 +949,7 @@ function mergeSearchCandidates(candidates, nowMs, currentChannelKey) {
|
|
|
949
949
|
}).map((candidate) => candidate.memory);
|
|
950
950
|
}
|
|
951
951
|
function createMemoryStore(db, context, options = {}) {
|
|
952
|
-
const
|
|
952
|
+
const runtimeContext2 = memoryRuntimeContextSchema.parse(context);
|
|
953
953
|
const parsedOptions = memoryStoreOptionsSchema.parse({ now: options.now });
|
|
954
954
|
const embedder = options.embedder;
|
|
955
955
|
const maxVectorDistance = options.maxVectorDistance;
|
|
@@ -961,14 +961,14 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
961
961
|
db,
|
|
962
962
|
limit: input.limit,
|
|
963
963
|
nowMs,
|
|
964
|
-
scopes: deriveVisibleMemoryScopes(
|
|
964
|
+
scopes: deriveVisibleMemoryScopes(runtimeContext2)
|
|
965
965
|
});
|
|
966
966
|
}
|
|
967
967
|
async function reuseDuplicateMemory(args) {
|
|
968
968
|
await rememberDuplicateIdempotency({
|
|
969
969
|
...args,
|
|
970
970
|
db,
|
|
971
|
-
runtimeContext
|
|
971
|
+
runtimeContext: runtimeContext2
|
|
972
972
|
});
|
|
973
973
|
await storeEmbedding({
|
|
974
974
|
content: args.duplicate.content,
|
|
@@ -983,8 +983,8 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
983
983
|
const input = createMemoryInputSchema.parse(rawInput);
|
|
984
984
|
const nowMs = getNowMs();
|
|
985
985
|
const content = normalizeContent(input.content);
|
|
986
|
-
const scope = deriveMemoryScope(
|
|
987
|
-
const subject = deriveMemorySubject(
|
|
986
|
+
const scope = deriveMemoryScope(runtimeContext2, scopeKind);
|
|
987
|
+
const subject = deriveMemorySubject(runtimeContext2, scope);
|
|
988
988
|
if (content.length > MAX_MEMORY_CONTENT_CHARS) {
|
|
989
989
|
throw new Error("Memory content exceeds the maximum length.");
|
|
990
990
|
}
|
|
@@ -1075,7 +1075,7 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1075
1075
|
candidates: preferenceCandidates,
|
|
1076
1076
|
content,
|
|
1077
1077
|
decider: supersessionDecider,
|
|
1078
|
-
runtimeContext
|
|
1078
|
+
runtimeContext: runtimeContext2
|
|
1079
1079
|
});
|
|
1080
1080
|
if (adjudication.decision === "duplicate") {
|
|
1081
1081
|
return await reuseDuplicateMemory({
|
|
@@ -1102,8 +1102,8 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1102
1102
|
observedAtMs: nowMs,
|
|
1103
1103
|
scope: scope.scope,
|
|
1104
1104
|
scopeKey: scope.scopeKey,
|
|
1105
|
-
sourceKey: sourceKey(
|
|
1106
|
-
sourcePlatform:
|
|
1105
|
+
sourceKey: sourceKey(runtimeContext2),
|
|
1106
|
+
sourcePlatform: runtimeContext2.source.platform,
|
|
1107
1107
|
subjectKey: subject.subjectKey,
|
|
1108
1108
|
subjectType: subject.subjectType,
|
|
1109
1109
|
kind: input.kind
|
|
@@ -1182,7 +1182,23 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1182
1182
|
async listMemories(input) {
|
|
1183
1183
|
input = listMemoriesInputSchema.parse(input);
|
|
1184
1184
|
const nowMs = getNowMs();
|
|
1185
|
-
const scopes = deriveVisibleMemoryScopes(
|
|
1185
|
+
const scopes = deriveVisibleMemoryScopes(runtimeContext2);
|
|
1186
|
+
await archiveExpiredMemoryBatch({
|
|
1187
|
+
db,
|
|
1188
|
+
nowMs,
|
|
1189
|
+
scopes
|
|
1190
|
+
});
|
|
1191
|
+
return await listVisibleMemories({
|
|
1192
|
+
db,
|
|
1193
|
+
limit: input.limit,
|
|
1194
|
+
nowMs,
|
|
1195
|
+
scopes
|
|
1196
|
+
});
|
|
1197
|
+
},
|
|
1198
|
+
async listPersonalMemories(input) {
|
|
1199
|
+
input = listMemoriesInputSchema.parse(input);
|
|
1200
|
+
const nowMs = getNowMs();
|
|
1201
|
+
const scopes = [deriveMemoryScope(runtimeContext2, "personal")];
|
|
1186
1202
|
await archiveExpiredMemoryBatch({
|
|
1187
1203
|
db,
|
|
1188
1204
|
nowMs,
|
|
@@ -1198,7 +1214,7 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1198
1214
|
async searchMemories(input) {
|
|
1199
1215
|
input = searchMemoriesInputSchema.parse(input);
|
|
1200
1216
|
const nowMs = getNowMs();
|
|
1201
|
-
const scopes = deriveVisibleMemoryScopes(
|
|
1217
|
+
const scopes = deriveVisibleMemoryScopes(runtimeContext2);
|
|
1202
1218
|
await archiveExpiredMemoryBatch({
|
|
1203
1219
|
db,
|
|
1204
1220
|
nowMs,
|
|
@@ -1228,13 +1244,13 @@ function createMemoryStore(db, context, options = {}) {
|
|
|
1228
1244
|
return mergeSearchCandidates(
|
|
1229
1245
|
[...vectorCandidates, ...lexicalCandidates],
|
|
1230
1246
|
nowMs,
|
|
1231
|
-
sourceChannelPrefix(
|
|
1247
|
+
sourceChannelPrefix(runtimeContext2)
|
|
1232
1248
|
).slice(0, limit);
|
|
1233
1249
|
},
|
|
1234
1250
|
async archiveMemory(input) {
|
|
1235
1251
|
input = archiveMemoryInputSchema.parse(input);
|
|
1236
1252
|
const nowMs = getNowMs();
|
|
1237
|
-
const scopes = deriveVisibleMemoryScopes(
|
|
1253
|
+
const scopes = deriveVisibleMemoryScopes(runtimeContext2);
|
|
1238
1254
|
const predicate = activeVisiblePredicate({ nowMs, scopes });
|
|
1239
1255
|
const idPrefix = input.id.trim();
|
|
1240
1256
|
if (!idPrefix) {
|
|
@@ -2072,7 +2088,7 @@ function createMemoryCreateTool(context) {
|
|
|
2072
2088
|
const parsedInput = parseMemoryToolInput(createMemoryInputSchema2, input);
|
|
2073
2089
|
const toolCallId = requireToolCallId(options.toolCallId);
|
|
2074
2090
|
const requestedExpiresAtMs = parseExpiresAt(parsedInput.expires_at);
|
|
2075
|
-
const
|
|
2091
|
+
const runtimeContext2 = memoryRuntimeContext(context);
|
|
2076
2092
|
const store = memoryStore(context, {
|
|
2077
2093
|
supersessionDecider: context.supersessionDecider
|
|
2078
2094
|
});
|
|
@@ -2083,7 +2099,7 @@ function createMemoryCreateTool(context) {
|
|
|
2083
2099
|
parseCreateMemoryRequest({
|
|
2084
2100
|
content: requireMemoryContent(parsedInput.content),
|
|
2085
2101
|
...requestedExpiresAtMs !== void 0 ? { expiresAtMs: requestedExpiresAtMs } : {},
|
|
2086
|
-
runtimeContext,
|
|
2102
|
+
runtimeContext: runtimeContext2,
|
|
2087
2103
|
...context.userText?.trim() ? {
|
|
2088
2104
|
sourceContext: {
|
|
2089
2105
|
currentUserText: context.userText.trim()
|
|
@@ -2309,13 +2325,13 @@ async function processMemorySession(context) {
|
|
|
2309
2325
|
if (!evidenceText) {
|
|
2310
2326
|
return;
|
|
2311
2327
|
}
|
|
2312
|
-
const
|
|
2328
|
+
const runtimeContext2 = memoryRuntimeContextSchema.parse({
|
|
2313
2329
|
conversationId: run.conversationId,
|
|
2314
2330
|
...run.actor ? { actor: run.actor } : {},
|
|
2315
2331
|
source: run.source
|
|
2316
2332
|
});
|
|
2317
2333
|
const agent = createMemoryAgent(context.model);
|
|
2318
|
-
const store = createMemoryStore(context.db,
|
|
2334
|
+
const store = createMemoryStore(context.db, runtimeContext2, {
|
|
2319
2335
|
embedder: context.embedder,
|
|
2320
2336
|
supersessionDecider: agent
|
|
2321
2337
|
});
|
|
@@ -2331,7 +2347,7 @@ async function processMemorySession(context) {
|
|
|
2331
2347
|
})),
|
|
2332
2348
|
actors: run.actors,
|
|
2333
2349
|
transcript,
|
|
2334
|
-
runtimeContext
|
|
2350
|
+
runtimeContext: runtimeContext2
|
|
2335
2351
|
});
|
|
2336
2352
|
});
|
|
2337
2353
|
if (memories.length === 0) {
|
|
@@ -2352,6 +2368,10 @@ async function processMemorySession(context) {
|
|
|
2352
2368
|
}
|
|
2353
2369
|
|
|
2354
2370
|
// src/recall.ts
|
|
2371
|
+
import {
|
|
2372
|
+
definePromptContext
|
|
2373
|
+
} from "@sentry/junior-plugin-api";
|
|
2374
|
+
import { z as z6 } from "zod";
|
|
2355
2375
|
var DEFAULT_RECALL_LIMIT = 5;
|
|
2356
2376
|
var MAX_PROMPT_CHARS = 4e3;
|
|
2357
2377
|
var MAX_MEMORY_LINE_CHARS = 600;
|
|
@@ -2365,48 +2385,151 @@ function trimContent(content, maxLength) {
|
|
|
2365
2385
|
function formatObservedDate(observedAtMs) {
|
|
2366
2386
|
return new Date(observedAtMs).toISOString().slice(0, 10);
|
|
2367
2387
|
}
|
|
2368
|
-
|
|
2388
|
+
var recalledMemorySchema = z6.object({
|
|
2389
|
+
id: z6.string().min(1),
|
|
2390
|
+
content: z6.string().min(1).max(MAX_MEMORY_LINE_CHARS),
|
|
2391
|
+
observedAtMs: z6.number().finite(),
|
|
2392
|
+
scope: z6.enum(["personal", "conversation"]),
|
|
2393
|
+
kind: z6.enum(["preference", "procedure", "knowledge"])
|
|
2394
|
+
}).strict();
|
|
2395
|
+
var memoryRecallContextSchema = z6.object({
|
|
2396
|
+
memories: z6.array(recalledMemorySchema).min(1).max(DEFAULT_RECALL_LIMIT)
|
|
2397
|
+
}).strict();
|
|
2398
|
+
function selectPromptMemories(memories) {
|
|
2369
2399
|
const header = "Relevant memories for this request:";
|
|
2370
2400
|
const footer = "Treat these as possibly stale context. Current user instructions and repository evidence take priority.";
|
|
2371
|
-
const
|
|
2401
|
+
const selected = [];
|
|
2372
2402
|
let totalChars = header.length + footer.length + 2;
|
|
2373
2403
|
for (const memory of memories) {
|
|
2374
|
-
const
|
|
2375
|
-
|
|
2376
|
-
MAX_MEMORY_LINE_CHARS
|
|
2377
|
-
)}`;
|
|
2404
|
+
const content = trimContent(memory.content, MAX_MEMORY_LINE_CHARS);
|
|
2405
|
+
const line = `- Observed ${formatObservedDate(memory.observedAtMs)}: ${content}`;
|
|
2378
2406
|
if (totalChars + line.length + 1 > MAX_PROMPT_CHARS) {
|
|
2379
2407
|
break;
|
|
2380
2408
|
}
|
|
2381
|
-
|
|
2409
|
+
selected.push({
|
|
2410
|
+
id: memory.id,
|
|
2411
|
+
content,
|
|
2412
|
+
observedAtMs: memory.observedAtMs,
|
|
2413
|
+
scope: memory.scope,
|
|
2414
|
+
kind: memory.kind
|
|
2415
|
+
});
|
|
2382
2416
|
totalChars += line.length + 1;
|
|
2383
2417
|
}
|
|
2384
|
-
|
|
2385
|
-
return void 0;
|
|
2386
|
-
}
|
|
2387
|
-
return `${header}
|
|
2388
|
-
${lines.join("\n")}
|
|
2389
|
-
|
|
2390
|
-
${footer}`;
|
|
2418
|
+
return selected;
|
|
2391
2419
|
}
|
|
2392
|
-
|
|
2420
|
+
function renderMemoryPrompt(memories) {
|
|
2421
|
+
return [
|
|
2422
|
+
"Relevant memories for this request:",
|
|
2423
|
+
...memories.map(
|
|
2424
|
+
(memory) => `- Observed ${formatObservedDate(memory.observedAtMs)}: ${memory.content}`
|
|
2425
|
+
),
|
|
2426
|
+
"",
|
|
2427
|
+
"Treat these as possibly stale context. Current user instructions and repository evidence take priority."
|
|
2428
|
+
].join("\n");
|
|
2429
|
+
}
|
|
2430
|
+
var memoryRecallContext = definePromptContext({
|
|
2431
|
+
kind: "recall",
|
|
2432
|
+
version: 1,
|
|
2433
|
+
schema: memoryRecallContextSchema,
|
|
2434
|
+
renderPrompt: (content) => renderMemoryPrompt(content.memories)
|
|
2435
|
+
});
|
|
2436
|
+
async function createMemoryPromptContributions(context) {
|
|
2393
2437
|
if (!context.text.trim()) {
|
|
2394
2438
|
return void 0;
|
|
2395
2439
|
}
|
|
2396
|
-
const
|
|
2440
|
+
const runtimeContext2 = memoryRuntimeContextSchema.parse({
|
|
2397
2441
|
...context.conversationId ? { conversationId: context.conversationId } : {},
|
|
2398
2442
|
...context.actor ? { actor: context.actor } : {},
|
|
2399
2443
|
source: context.source
|
|
2400
2444
|
});
|
|
2401
|
-
const memories = await createMemoryStore(context.db,
|
|
2445
|
+
const memories = await createMemoryStore(context.db, runtimeContext2, {
|
|
2402
2446
|
...context.embedder ? { embedder: context.embedder } : {},
|
|
2403
2447
|
...context.maxVectorDistance !== void 0 ? { maxVectorDistance: context.maxVectorDistance } : {}
|
|
2404
2448
|
}).searchMemories({
|
|
2405
2449
|
query: context.text,
|
|
2406
2450
|
limit: DEFAULT_RECALL_LIMIT
|
|
2407
2451
|
});
|
|
2408
|
-
const
|
|
2409
|
-
|
|
2452
|
+
const selected = selectPromptMemories(memories);
|
|
2453
|
+
if (selected.length === 0) {
|
|
2454
|
+
return void 0;
|
|
2455
|
+
}
|
|
2456
|
+
if (selected.some((memory) => memory.scope === "personal")) {
|
|
2457
|
+
return [{ text: renderMemoryPrompt(selected) }];
|
|
2458
|
+
}
|
|
2459
|
+
return [memoryRecallContext({ memories: selected })];
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// src/user-pages.ts
|
|
2463
|
+
var MEMORY_PAGE_LIMIT = 100;
|
|
2464
|
+
function runtimeContext(actor) {
|
|
2465
|
+
if (actor.platform === "slack") {
|
|
2466
|
+
return {
|
|
2467
|
+
actor,
|
|
2468
|
+
source: {
|
|
2469
|
+
platform: "slack",
|
|
2470
|
+
type: "priv",
|
|
2471
|
+
teamId: actor.teamId,
|
|
2472
|
+
channelId: "DDASHBOARD"
|
|
2473
|
+
}
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
return {
|
|
2477
|
+
actor,
|
|
2478
|
+
source: {
|
|
2479
|
+
platform: "local",
|
|
2480
|
+
type: "priv",
|
|
2481
|
+
conversationId: "local:dashboard:memories"
|
|
2482
|
+
}
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
function memoryKindLabel(kind) {
|
|
2486
|
+
return kind.charAt(0).toUpperCase() + kind.slice(1);
|
|
2487
|
+
}
|
|
2488
|
+
function rememberedDate(createdAtMs) {
|
|
2489
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
2490
|
+
dateStyle: "medium",
|
|
2491
|
+
timeZone: "UTC"
|
|
2492
|
+
}).format(new Date(createdAtMs));
|
|
2493
|
+
}
|
|
2494
|
+
async function readPersonalMemories(db, actors) {
|
|
2495
|
+
const memories = await Promise.all(
|
|
2496
|
+
actors.flatMap((actor) => {
|
|
2497
|
+
const context = runtimeContext(actor);
|
|
2498
|
+
return context ? [
|
|
2499
|
+
createMemoryStore(db, context).listPersonalMemories({
|
|
2500
|
+
limit: MEMORY_PAGE_LIMIT
|
|
2501
|
+
})
|
|
2502
|
+
] : [];
|
|
2503
|
+
})
|
|
2504
|
+
);
|
|
2505
|
+
return [
|
|
2506
|
+
...new Map(memories.flat().map((memory) => [memory.id, memory])).values()
|
|
2507
|
+
].sort((left, right) => right.createdAtMs - left.createdAtMs).slice(0, MEMORY_PAGE_LIMIT);
|
|
2508
|
+
}
|
|
2509
|
+
function createMemoryUserPage() {
|
|
2510
|
+
return {
|
|
2511
|
+
id: "memories",
|
|
2512
|
+
label: "Memories",
|
|
2513
|
+
description: "Personal facts and preferences Junior remembers about you.",
|
|
2514
|
+
async read(ctx) {
|
|
2515
|
+
const memories = await readPersonalMemories(
|
|
2516
|
+
ctx.db,
|
|
2517
|
+
ctx.viewer.actors
|
|
2518
|
+
);
|
|
2519
|
+
return {
|
|
2520
|
+
type: "list",
|
|
2521
|
+
emptyText: "No personal memories yet.",
|
|
2522
|
+
records: memories.map((memory) => ({
|
|
2523
|
+
id: memory.id,
|
|
2524
|
+
title: memory.content,
|
|
2525
|
+
metadata: [
|
|
2526
|
+
{ label: "Type", value: memoryKindLabel(memory.kind) },
|
|
2527
|
+
{ label: "Remembered", value: rememberedDate(memory.createdAtMs) }
|
|
2528
|
+
]
|
|
2529
|
+
}))
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
};
|
|
2410
2533
|
}
|
|
2411
2534
|
|
|
2412
2535
|
// src/plugin.ts
|
|
@@ -2471,6 +2594,7 @@ function createMemoryPlugin(options = {}) {
|
|
|
2471
2594
|
}
|
|
2472
2595
|
}
|
|
2473
2596
|
},
|
|
2597
|
+
userPages: [createMemoryUserPage()],
|
|
2474
2598
|
hooks: {
|
|
2475
2599
|
tools(ctx) {
|
|
2476
2600
|
const agent = createMemoryAgent(ctx.model);
|
|
@@ -2496,7 +2620,7 @@ function createMemoryPlugin(options = {}) {
|
|
|
2496
2620
|
};
|
|
2497
2621
|
},
|
|
2498
2622
|
async userPrompt(ctx) {
|
|
2499
|
-
return await
|
|
2623
|
+
return await createMemoryPromptContributions({
|
|
2500
2624
|
...ctx.conversationId ? { conversationId: ctx.conversationId } : {},
|
|
2501
2625
|
...ctx.actor ? { actor: ctx.actor } : {},
|
|
2502
2626
|
db: ctx.db,
|