@sentry/junior-memory 0.91.0 → 0.92.1
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 +47 -5
- package/dist/index.js +165 -74
- package/dist/index.js.map +1 -1
- package/dist/recall.d.ts +2 -2
- package/dist/store.d.ts +1 -1
- package/dist/tools.d.ts +2 -2
- package/dist/types.d.ts +4 -4
- package/package.json +2 -2
- package/src/agent.ts +106 -31
- package/src/plugin.ts +6 -5
- package/src/process-session.ts +116 -18
- package/src/recall.ts +9 -17
- package/src/scope.ts +11 -11
- package/src/store.ts +1 -1
- package/src/tools.ts +8 -8
- package/src/types.ts +4 -4
package/dist/agent.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PluginModel } from "@sentry/junior-plugin-api";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import type { MemorySupersessionDecision, MemorySupersessionInput } from "./store";
|
|
4
4
|
declare const createMemoryRequestSchema: z.ZodObject<{
|
|
@@ -6,7 +6,7 @@ declare const createMemoryRequestSchema: z.ZodObject<{
|
|
|
6
6
|
expiresAtMs: z.ZodOptional<z.ZodNumber>;
|
|
7
7
|
runtimeContext: z.ZodUnion<readonly [z.ZodObject<{
|
|
8
8
|
conversationId: z.ZodOptional<z.ZodString>;
|
|
9
|
-
|
|
9
|
+
actor: z.ZodOptional<z.ZodObject<{
|
|
10
10
|
platform: z.ZodLiteral<"slack">;
|
|
11
11
|
teamId: z.ZodString;
|
|
12
12
|
email: z.ZodOptional<z.ZodString>;
|
|
@@ -27,7 +27,7 @@ declare const createMemoryRequestSchema: z.ZodObject<{
|
|
|
27
27
|
}, z.core.$strict>;
|
|
28
28
|
}, z.core.$strict>, z.ZodObject<{
|
|
29
29
|
conversationId: z.ZodOptional<z.ZodString>;
|
|
30
|
-
|
|
30
|
+
actor: z.ZodOptional<z.ZodObject<{
|
|
31
31
|
platform: z.ZodLiteral<"local">;
|
|
32
32
|
email: z.ZodOptional<z.ZodString>;
|
|
33
33
|
fullName: z.ZodOptional<z.ZodString>;
|
|
@@ -48,9 +48,26 @@ declare const extractSessionRequestSchema: z.ZodObject<{
|
|
|
48
48
|
existingMemories: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
49
49
|
content: z.ZodString;
|
|
50
50
|
}, z.core.$strict>>>;
|
|
51
|
+
actors: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
52
|
+
platform: z.ZodLiteral<"slack">;
|
|
53
|
+
teamId: z.ZodString;
|
|
54
|
+
email: z.ZodOptional<z.ZodString>;
|
|
55
|
+
fullName: z.ZodOptional<z.ZodString>;
|
|
56
|
+
userId: z.ZodString;
|
|
57
|
+
userName: z.ZodOptional<z.ZodString>;
|
|
58
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
59
|
+
platform: z.ZodLiteral<"local">;
|
|
60
|
+
email: z.ZodOptional<z.ZodString>;
|
|
61
|
+
fullName: z.ZodOptional<z.ZodString>;
|
|
62
|
+
userId: z.ZodString;
|
|
63
|
+
userName: z.ZodOptional<z.ZodString>;
|
|
64
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
65
|
+
platform: z.ZodLiteral<"system">;
|
|
66
|
+
name: z.ZodString;
|
|
67
|
+
}, z.core.$strict>], "platform">>;
|
|
51
68
|
runtimeContext: z.ZodUnion<readonly [z.ZodObject<{
|
|
52
69
|
conversationId: z.ZodOptional<z.ZodString>;
|
|
53
|
-
|
|
70
|
+
actor: z.ZodOptional<z.ZodObject<{
|
|
54
71
|
platform: z.ZodLiteral<"slack">;
|
|
55
72
|
teamId: z.ZodString;
|
|
56
73
|
email: z.ZodOptional<z.ZodString>;
|
|
@@ -71,7 +88,7 @@ declare const extractSessionRequestSchema: z.ZodObject<{
|
|
|
71
88
|
}, z.core.$strict>;
|
|
72
89
|
}, z.core.$strict>, z.ZodObject<{
|
|
73
90
|
conversationId: z.ZodOptional<z.ZodString>;
|
|
74
|
-
|
|
91
|
+
actor: z.ZodOptional<z.ZodObject<{
|
|
75
92
|
platform: z.ZodLiteral<"local">;
|
|
76
93
|
email: z.ZodOptional<z.ZodString>;
|
|
77
94
|
fullName: z.ZodOptional<z.ZodString>;
|
|
@@ -91,6 +108,30 @@ declare const extractSessionRequestSchema: z.ZodObject<{
|
|
|
91
108
|
assistant: "assistant";
|
|
92
109
|
}>;
|
|
93
110
|
text: z.ZodString;
|
|
111
|
+
provenance: z.ZodOptional<z.ZodObject<{
|
|
112
|
+
authority: z.ZodEnum<{
|
|
113
|
+
instruction: "instruction";
|
|
114
|
+
context: "context";
|
|
115
|
+
}>;
|
|
116
|
+
actor: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
117
|
+
platform: z.ZodLiteral<"slack">;
|
|
118
|
+
teamId: z.ZodString;
|
|
119
|
+
email: z.ZodOptional<z.ZodString>;
|
|
120
|
+
fullName: z.ZodOptional<z.ZodString>;
|
|
121
|
+
userId: z.ZodString;
|
|
122
|
+
userName: z.ZodOptional<z.ZodString>;
|
|
123
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
124
|
+
platform: z.ZodLiteral<"local">;
|
|
125
|
+
email: z.ZodOptional<z.ZodString>;
|
|
126
|
+
fullName: z.ZodOptional<z.ZodString>;
|
|
127
|
+
userId: z.ZodString;
|
|
128
|
+
userName: z.ZodOptional<z.ZodString>;
|
|
129
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
130
|
+
platform: z.ZodLiteral<"system">;
|
|
131
|
+
name: z.ZodString;
|
|
132
|
+
}, z.core.$strict>], "platform">>;
|
|
133
|
+
}, z.core.$strict>>;
|
|
134
|
+
isRunActor: z.ZodOptional<z.ZodBoolean>;
|
|
94
135
|
}, z.core.$strict>, z.ZodObject<{
|
|
95
136
|
type: z.ZodLiteral<"toolResult">;
|
|
96
137
|
toolName: z.ZodString;
|
|
@@ -128,6 +169,7 @@ declare const extractedMemoryResultSchema: z.ZodObject<{
|
|
|
128
169
|
procedure: "procedure";
|
|
129
170
|
knowledge: "knowledge";
|
|
130
171
|
}>;
|
|
172
|
+
evidenceMessageIndices: z.ZodArray<z.ZodNumber>;
|
|
131
173
|
}, z.core.$strict>;
|
|
132
174
|
export type MemoryReview = z.output<typeof memoryReviewDecisionSchema>;
|
|
133
175
|
export type CreateMemoryRequest = z.output<typeof createMemoryRequestSchema>;
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import { defineJuniorPlugin } from "@sentry/junior-plugin-api";
|
|
3
3
|
|
|
4
4
|
// src/agent.ts
|
|
5
|
+
import { actorSchema } from "@sentry/junior-plugin-api";
|
|
5
6
|
import { z as z2 } from "zod";
|
|
6
7
|
|
|
7
8
|
// src/types.ts
|
|
8
9
|
import {
|
|
9
|
-
|
|
10
|
+
localActorSchema,
|
|
10
11
|
localSourceSchema,
|
|
11
|
-
|
|
12
|
+
slackActorSchema,
|
|
12
13
|
slackSourceSchema
|
|
13
14
|
} from "@sentry/junior-plugin-api";
|
|
14
15
|
import { z } from "zod";
|
|
@@ -28,12 +29,12 @@ var MEMORY_EMBEDDING_DIMENSIONS = 1536;
|
|
|
28
29
|
var nonEmptyStringSchema = z.string().min(1);
|
|
29
30
|
var slackMemoryRuntimeContextSchema = z.object({
|
|
30
31
|
conversationId: nonEmptyStringSchema.optional(),
|
|
31
|
-
|
|
32
|
+
actor: slackActorSchema.optional(),
|
|
32
33
|
source: slackSourceSchema
|
|
33
34
|
}).strict();
|
|
34
35
|
var localMemoryRuntimeContextSchema = z.object({
|
|
35
36
|
conversationId: nonEmptyStringSchema.optional(),
|
|
36
|
-
|
|
37
|
+
actor: localActorSchema.optional(),
|
|
37
38
|
source: localSourceSchema
|
|
38
39
|
}).strict();
|
|
39
40
|
var memoryRuntimeContextSchema = z.union([
|
|
@@ -61,19 +62,27 @@ var createMemoryRequestSchema = z2.object({
|
|
|
61
62
|
currentUserText: z2.string().min(1).optional()
|
|
62
63
|
}).strict().optional()
|
|
63
64
|
}).strict();
|
|
65
|
+
var transcriptProvenanceSchema = z2.object({
|
|
66
|
+
authority: z2.enum(["instruction", "context"]),
|
|
67
|
+
actor: actorSchema.optional()
|
|
68
|
+
}).strict();
|
|
69
|
+
var evidenceMessageIndicesSchema = z2.array(z2.number().int().nonnegative()).min(1).max(10).describe("Indices from <run-transcript> that directly support this memory.");
|
|
64
70
|
var extractSessionRequestSchema = z2.object({
|
|
65
71
|
existingMemories: z2.array(
|
|
66
72
|
z2.object({
|
|
67
73
|
content: z2.string().min(1)
|
|
68
74
|
}).strict()
|
|
69
75
|
).max(10).default([]),
|
|
76
|
+
actors: z2.array(actorSchema),
|
|
70
77
|
runtimeContext: memoryRuntimeContextSchema,
|
|
71
78
|
transcript: z2.array(
|
|
72
79
|
z2.discriminatedUnion("type", [
|
|
73
80
|
z2.object({
|
|
74
81
|
type: z2.literal("message"),
|
|
75
82
|
role: z2.enum(["user", "assistant"]),
|
|
76
|
-
text: z2.string().min(1)
|
|
83
|
+
text: z2.string().min(1),
|
|
84
|
+
provenance: transcriptProvenanceSchema.optional(),
|
|
85
|
+
isRunActor: z2.boolean().optional()
|
|
77
86
|
}).strict(),
|
|
78
87
|
z2.object({
|
|
79
88
|
type: z2.literal("toolResult"),
|
|
@@ -116,10 +125,10 @@ var memoryReviewResponseSchema = z2.discriminatedUnion("decision", [
|
|
|
116
125
|
z2.object({
|
|
117
126
|
decision: z2.literal("store"),
|
|
118
127
|
kind: memoryKindSchema.describe(
|
|
119
|
-
"Use preference only for
|
|
128
|
+
"Use preference only for actor-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."
|
|
120
129
|
),
|
|
121
130
|
canonicalFact: z2.string().min(1).describe(
|
|
122
|
-
"Stored memory text. It must be self-contained and must not include
|
|
131
|
+
"Stored memory text. It must be self-contained and must not include actor names, actor/user labels, source labels, or first- or second-person wording."
|
|
123
132
|
),
|
|
124
133
|
expiresAtMs: expiresAtMsSchema
|
|
125
134
|
}).strict(),
|
|
@@ -130,17 +139,19 @@ var memoryReviewResponseSchema = z2.discriminatedUnion("decision", [
|
|
|
130
139
|
]);
|
|
131
140
|
var extractedMemorySchema = z2.object({
|
|
132
141
|
kind: memoryKindSchema.describe(
|
|
133
|
-
"Use preference only for
|
|
142
|
+
"Use preference only for actor-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."
|
|
134
143
|
),
|
|
135
144
|
canonicalFact: z2.string().min(1).describe(
|
|
136
|
-
"Stored memory text as one self-contained fact. It must not include
|
|
145
|
+
"Stored memory text as one self-contained fact. It must not include actor names, actor/user labels, source labels, or first- or second-person wording."
|
|
137
146
|
),
|
|
138
|
-
expiresAtMs: expiresAtMsSchema
|
|
147
|
+
expiresAtMs: expiresAtMsSchema,
|
|
148
|
+
evidenceMessageIndices: evidenceMessageIndicesSchema
|
|
139
149
|
}).strict();
|
|
140
150
|
var extractedMemoryResultSchema = z2.object({
|
|
141
151
|
content: z2.string().min(1),
|
|
142
152
|
expiresAtMs: expiresAtMsSchema,
|
|
143
|
-
kind: memoryKindSchema
|
|
153
|
+
kind: memoryKindSchema,
|
|
154
|
+
evidenceMessageIndices: evidenceMessageIndicesSchema
|
|
144
155
|
}).strict();
|
|
145
156
|
var extractMemoriesResponseSchema = z2.object({
|
|
146
157
|
memories: z2.array(extractedMemorySchema).max(5).describe(
|
|
@@ -172,7 +183,7 @@ var MEMORY_EXTRACTION_SYSTEM = [
|
|
|
172
183
|
].join("\n");
|
|
173
184
|
var MEMORY_SUPERSESSION_SYSTEM = [
|
|
174
185
|
"You are Junior's memory supersession agent.",
|
|
175
|
-
"Decide whether a new
|
|
186
|
+
"Decide whether a new actor preference clearly replaces existing active actor preferences.",
|
|
176
187
|
"Return supersedes_old only for obvious changed preferences about the same mutable slot.",
|
|
177
188
|
"If the facts are additive, different topics, duplicate, broader/narrower without direct replacement, or uncertain, do not supersede."
|
|
178
189
|
].join("\n");
|
|
@@ -181,19 +192,19 @@ var CANONICAL_CONTENT_RULES = [
|
|
|
181
192
|
"- Store the minimum useful assertion supported by source evidence; do not add adjacent steps, caveats, or generalized advice.",
|
|
182
193
|
"- Do not return both concise and expanded variants of the same source assertion; keep the shortest self-contained canonical memory.",
|
|
183
194
|
"- Put ownership in structured fields, not prose.",
|
|
184
|
-
"- For
|
|
195
|
+
"- For actor memories, omit the subject and write a stable fact such as 'Prefers X', 'Uses Y', or 'Thinks Z'.",
|
|
185
196
|
"- Drop perspective/provenance markers while preserving useful context.",
|
|
186
|
-
"- Remove
|
|
197
|
+
"- Remove actor names, display names, actor/user labels, first- or second-person wording, thread labels, channel labels, and source labels."
|
|
187
198
|
];
|
|
188
199
|
function escapeXml(value) {
|
|
189
200
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
190
201
|
}
|
|
191
202
|
function runtimeDescription(request) {
|
|
192
203
|
const runtime = request.runtimeContext;
|
|
193
|
-
const
|
|
204
|
+
const actor = runtime.actor?.platform === "slack" ? `slack:${runtime.actor.teamId}:${runtime.actor.userId}` : runtime.actor?.platform === "local" ? `local:${runtime.actor.userId}` : "none";
|
|
194
205
|
const source = runtime.source.platform === "slack" ? `slack:${runtime.source.teamId}:${runtime.source.channelId}` : `local:${runtime.source.conversationId}`;
|
|
195
206
|
const lines = [
|
|
196
|
-
`-
|
|
207
|
+
`- actor: ${escapeXml(actor)}`,
|
|
197
208
|
`- source: ${escapeXml(source)}`,
|
|
198
209
|
`- has_conversation: ${runtime.conversationId ? "true" : "false"}`,
|
|
199
210
|
`- expires_at: ${request.expiresAtMs === void 0 ? "never" : escapeXml(new Date(request.expiresAtMs).toISOString())}`
|
|
@@ -225,14 +236,22 @@ function existingMemoriesContext(request) {
|
|
|
225
236
|
"</existing-memories>"
|
|
226
237
|
].join("\n");
|
|
227
238
|
}
|
|
228
|
-
function
|
|
229
|
-
return [
|
|
230
|
-
|
|
231
|
-
|
|
239
|
+
function allowedExtractionKinds(actorCount) {
|
|
240
|
+
return actorCount === 1 ? new Set(MEMORY_KINDS) : /* @__PURE__ */ new Set(["procedure", "knowledge"]);
|
|
241
|
+
}
|
|
242
|
+
function memoryKindsContext(allowedKinds) {
|
|
243
|
+
const lines = ["<memory-kinds>"];
|
|
244
|
+
if (allowedKinds.has("preference")) {
|
|
245
|
+
lines.push(
|
|
246
|
+
"- preference: a durable first-person personal preference, opinion, habit, or workflow owned by the current actor. Stored as actor memory."
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
lines.push(
|
|
232
250
|
"- 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.",
|
|
233
|
-
"- knowledge: stable shared project, channel, operational, or runbook fact that is not a personal
|
|
251
|
+
"- knowledge: stable shared project, channel, operational, or runbook fact that is not a personal actor preference. Direct answers to user inquiries qualify only when they are durable beyond this run. Stored as conversation memory.",
|
|
234
252
|
"</memory-kinds>"
|
|
235
|
-
|
|
253
|
+
);
|
|
254
|
+
return lines.join("\n");
|
|
236
255
|
}
|
|
237
256
|
function reviewPrompt(request) {
|
|
238
257
|
const sections = [
|
|
@@ -250,17 +269,17 @@ function reviewPrompt(request) {
|
|
|
250
269
|
"<rules>",
|
|
251
270
|
"- Return store only when the candidate is public/shareable, durable, and self-contained.",
|
|
252
271
|
"- First classify the memory kind: preference, procedure, or knowledge.",
|
|
253
|
-
"- Use kind=preference only for first-person facts authored by the current
|
|
254
|
-
"- 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
|
|
272
|
+
"- Use kind=preference only for first-person facts authored by the current actor about their own preference, opinion, habit, identity, or workflow.",
|
|
273
|
+
"- 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 actor.",
|
|
255
274
|
"- Use kind=procedure for reusable task/process/runbook instructions.",
|
|
256
275
|
"- Use kind=knowledge for shared project, channel, operational, or runbook facts.",
|
|
257
276
|
"- 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.",
|
|
258
|
-
"- A candidate may be badly phrased by an outer assistant or extraction pass. When current-user-message contains the
|
|
277
|
+
"- A candidate may be badly phrased by an outer assistant or extraction pass. When current-user-message contains the actor's own first-person memory fact, treat that as actor-authored source evidence and canonicalize the fact instead of rejecting for third-person wording.",
|
|
259
278
|
"- 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.",
|
|
260
279
|
"- 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.",
|
|
261
280
|
"- Store content as person-less, source-less canonical knowledge. Ownership and source live in structured metadata, not prose.",
|
|
262
|
-
"- For
|
|
263
|
-
"- Remove
|
|
281
|
+
"- For actor memories, omit the subject and write the content as a stable fact such as 'Prefers X', 'Uses Y', or 'Thinks Z'.",
|
|
282
|
+
"- Remove actor names, display names, actor/user labels, first- or second-person wording, thread labels, channel labels, and source labels from stored content.",
|
|
264
283
|
"- Reject third-party personal profile facts, even if they mention a name.",
|
|
265
284
|
"- Reject vague content such as 'remember this' unless the candidate or current-user-message contains the concrete fact.",
|
|
266
285
|
"- Preserve the requested expiration when one exists; otherwise set expiresAtMs to null.",
|
|
@@ -270,6 +289,15 @@ function reviewPrompt(request) {
|
|
|
270
289
|
].filter((section) => section !== void 0);
|
|
271
290
|
return sections.join("\n");
|
|
272
291
|
}
|
|
292
|
+
function transcriptActorLabel(actor) {
|
|
293
|
+
if (!actor) {
|
|
294
|
+
return "none";
|
|
295
|
+
}
|
|
296
|
+
if (actor.platform === "system") {
|
|
297
|
+
return `system:${actor.name}`;
|
|
298
|
+
}
|
|
299
|
+
return actor.platform === "slack" ? `slack:${actor.teamId}:${actor.userId}` : `local:${actor.userId}`;
|
|
300
|
+
}
|
|
273
301
|
function runTranscriptContext(request) {
|
|
274
302
|
return [
|
|
275
303
|
"<run-transcript>",
|
|
@@ -281,8 +309,11 @@ function runTranscriptContext(request) {
|
|
|
281
309
|
"</tool-result>"
|
|
282
310
|
].join("\n");
|
|
283
311
|
}
|
|
312
|
+
const authority = entry.provenance?.authority ?? "context";
|
|
313
|
+
const isRunActor = entry.isRunActor === true;
|
|
314
|
+
const actor = transcriptActorLabel(entry.provenance?.actor);
|
|
284
315
|
return [
|
|
285
|
-
`<message index="${index2}" role="${entry.role}">`,
|
|
316
|
+
`<message index="${index2}" role="${entry.role}" authority="${authority}" is_run_actor="${isRunActor ? "true" : "false"}" actor="${escapeXml(actor)}">`,
|
|
286
317
|
escapeXml(entry.text),
|
|
287
318
|
"</message>"
|
|
288
319
|
].join("\n");
|
|
@@ -291,6 +322,8 @@ function runTranscriptContext(request) {
|
|
|
291
322
|
].join("\n");
|
|
292
323
|
}
|
|
293
324
|
function sessionExtractionPrompt(request) {
|
|
325
|
+
const allowedKinds = allowedExtractionKinds(request.actors.length);
|
|
326
|
+
const allowsPreference = allowedKinds.has("preference");
|
|
294
327
|
return [
|
|
295
328
|
"<memory-extraction-input>",
|
|
296
329
|
"Extract durable memories from this completed agent run using the runtime-owned context below.",
|
|
@@ -301,12 +334,19 @@ function sessionExtractionPrompt(request) {
|
|
|
301
334
|
"",
|
|
302
335
|
existingMemoriesContext(request),
|
|
303
336
|
"",
|
|
304
|
-
memoryKindsContext(),
|
|
337
|
+
memoryKindsContext(allowedKinds),
|
|
305
338
|
"",
|
|
306
339
|
runTranscriptContext(request),
|
|
307
340
|
"",
|
|
308
341
|
"<rules>",
|
|
309
342
|
"- Return at most five memories.",
|
|
343
|
+
"- Every returned memory must cite one or more evidenceMessageIndices from <run-transcript>.",
|
|
344
|
+
"- Cite only indices that directly support the stored fact; do not cite assistant messages as independent evidence.",
|
|
345
|
+
"- Each transcript message exposes authority (instruction or context), is_run_actor, and an actor id. Use these to classify evidence.",
|
|
346
|
+
...allowsPreference ? [
|
|
347
|
+
"- For a preference, cite only messages with authority=instruction and is_run_actor=true; a preference must be the run actor's own first-person fact."
|
|
348
|
+
] : [],
|
|
349
|
+
"- For a procedure or knowledge memory, cite run-actor instruction messages, public context messages, or successful tool results.",
|
|
310
350
|
"- Use user messages and successful tool results as source evidence for storable facts.",
|
|
311
351
|
"- Use failed tool results only when the failure reveals durable process knowledge, not transient errors.",
|
|
312
352
|
"- Use assistant messages only as context; do not store the assistant's claims unless supported by user messages or tool results.",
|
|
@@ -318,9 +358,18 @@ function sessionExtractionPrompt(request) {
|
|
|
318
358
|
"- 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.",
|
|
319
359
|
"- Set kind=procedure for reusable task/process/runbook instructions.",
|
|
320
360
|
"- Set kind=knowledge for shared team, project, channel, runbook, or operational facts.",
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
361
|
+
...allowsPreference ? [
|
|
362
|
+
"- Set kind=preference only for clear durable first-person facts authored by the current actor about their own preference, opinion, habit, identity, or workflow.",
|
|
363
|
+
"- A single task request or ask-for-help is never a durable preference, even when phrased as an ongoing action for this run (for example 'help me capture takeaways as we go'). Do not convert a one-off ask into a 'Prefers ...' memory.",
|
|
364
|
+
"- A durable preference requires explicitly stated, generalizable first-person phrasing such as 'I prefer ...', 'I always ...', or 'I never ...' that describes how the actor wants things done in general, not just for the current task."
|
|
365
|
+
] : [
|
|
366
|
+
"- This completed run has multiple run actors. Return only conversation-scoped procedure or knowledge memories.",
|
|
367
|
+
"- Do not return personal preferences, opinions, habits, identity facts, or workflow preferences from any actor in this run.",
|
|
368
|
+
"- Do not convert a personal first-person statement into shared knowledge or procedure. Statements like 'I prefer ...', 'I use ...', 'I always ...', or 'I never ...' are not memory evidence in this run.",
|
|
369
|
+
"- Shared team, channel, repository, or operational norms are eligible only when the source states them as collective practice or durable operational fact, not as one individual's preference."
|
|
370
|
+
],
|
|
371
|
+
"- 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 actor.",
|
|
372
|
+
"- User-authored task instructions are procedures, not preferences, unless they explicitly describe the actor's personal preference or habit.",
|
|
324
373
|
"- Procedural statements such as 'for X, do Y', 'when X, do Y', and 'to accomplish X, do Y' belong in procedures.",
|
|
325
374
|
...CANONICAL_CONTENT_RULES,
|
|
326
375
|
"- Skip a candidate when existing-memories already cover the same durable fact.",
|
|
@@ -417,7 +466,8 @@ function extractedMemoriesFromResponse(response) {
|
|
|
417
466
|
const toMemory = (memory) => parseExtractedMemory({
|
|
418
467
|
content: memory.canonicalFact,
|
|
419
468
|
expiresAtMs: memory.expiresAtMs,
|
|
420
|
-
kind: memory.kind
|
|
469
|
+
kind: memory.kind,
|
|
470
|
+
evidenceMessageIndices: memory.evidenceMessageIndices
|
|
421
471
|
});
|
|
422
472
|
return response.memories.map(toMemory);
|
|
423
473
|
}
|
|
@@ -709,21 +759,21 @@ function sourceConversationKey(ctx) {
|
|
|
709
759
|
}
|
|
710
760
|
return `slack:${ctx.source.teamId}:${ctx.source.channelId}:${threadKey}`;
|
|
711
761
|
}
|
|
712
|
-
function
|
|
713
|
-
const
|
|
714
|
-
if (!
|
|
762
|
+
function actorScopeKey(ctx) {
|
|
763
|
+
const actor = ctx.actor;
|
|
764
|
+
if (!actor?.userId) {
|
|
715
765
|
return void 0;
|
|
716
766
|
}
|
|
717
|
-
if (
|
|
718
|
-
return `slack:${
|
|
767
|
+
if (actor.platform === "slack") {
|
|
768
|
+
return `slack:${actor.teamId}:${actor.userId}`;
|
|
719
769
|
}
|
|
720
|
-
return `local:${
|
|
770
|
+
return `local:${actor.userId}`;
|
|
721
771
|
}
|
|
722
772
|
function deriveMemoryScope(ctx, scope) {
|
|
723
773
|
if (scope === "personal") {
|
|
724
|
-
const scopeKey2 =
|
|
774
|
+
const scopeKey2 = actorScopeKey(ctx);
|
|
725
775
|
if (!scopeKey2) {
|
|
726
|
-
throw new Error("Personal memory requires
|
|
776
|
+
throw new Error("Personal memory requires actor context.");
|
|
727
777
|
}
|
|
728
778
|
return { scope, scopeKey: scopeKey2 };
|
|
729
779
|
}
|
|
@@ -735,9 +785,9 @@ function deriveMemoryScope(ctx, scope) {
|
|
|
735
785
|
}
|
|
736
786
|
function deriveMemorySubject(ctx, scope) {
|
|
737
787
|
if (scope.scope === "personal") {
|
|
738
|
-
const subjectKey2 =
|
|
788
|
+
const subjectKey2 = actorScopeKey(ctx);
|
|
739
789
|
if (!subjectKey2) {
|
|
740
|
-
throw new Error("User-subject memory requires
|
|
790
|
+
throw new Error("User-subject memory requires actor context.");
|
|
741
791
|
}
|
|
742
792
|
return { subjectType: "user", subjectKey: subjectKey2 };
|
|
743
793
|
}
|
|
@@ -1780,8 +1830,8 @@ var KNOWN_TOOL_INPUT_ERROR_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
1780
1830
|
"Memory id is required.",
|
|
1781
1831
|
"Memory was not found in the current context.",
|
|
1782
1832
|
"Memory id prefix is ambiguous.",
|
|
1783
|
-
"Personal memory requires
|
|
1784
|
-
"User-subject memory requires
|
|
1833
|
+
"Personal memory requires actor context.",
|
|
1834
|
+
"User-subject memory requires actor context."
|
|
1785
1835
|
]);
|
|
1786
1836
|
function throwToolInputError(message) {
|
|
1787
1837
|
throw new PluginToolInputError(message);
|
|
@@ -1798,7 +1848,7 @@ function asToolInputError(error) {
|
|
|
1798
1848
|
function memoryRuntimeContext(context) {
|
|
1799
1849
|
return memoryRuntimeContextSchema.parse({
|
|
1800
1850
|
...context.conversationId ? { conversationId: context.conversationId } : {},
|
|
1801
|
-
...context.
|
|
1851
|
+
...context.actor ? { actor: context.actor } : {},
|
|
1802
1852
|
source: context.source
|
|
1803
1853
|
});
|
|
1804
1854
|
}
|
|
@@ -1988,7 +2038,7 @@ function createInput(context, input, toolCallId) {
|
|
|
1988
2038
|
}
|
|
1989
2039
|
function targetForKind(kind) {
|
|
1990
2040
|
if (kind === "preference") {
|
|
1991
|
-
return "
|
|
2041
|
+
return "actor";
|
|
1992
2042
|
}
|
|
1993
2043
|
return "conversation";
|
|
1994
2044
|
}
|
|
@@ -2125,7 +2175,7 @@ function createMemoryListTool(context) {
|
|
|
2125
2175
|
}
|
|
2126
2176
|
function createMemorySearchTool(context) {
|
|
2127
2177
|
return definePluginTool({
|
|
2128
|
-
description: "Search active memories visible in the current context. Use when the model needs targeted memory recall. The tool searches only the current
|
|
2178
|
+
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.",
|
|
2129
2179
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
2130
2180
|
inputSchema: searchMemoriesInputSchema2,
|
|
2131
2181
|
outputSchema: memoryManyOutputSchema,
|
|
@@ -2163,22 +2213,61 @@ var extractedMemoryCacheSchema = z5.array(
|
|
|
2163
2213
|
z5.object({
|
|
2164
2214
|
content: z5.string().min(1),
|
|
2165
2215
|
expiresAtMs: z5.number().finite().nullable(),
|
|
2166
|
-
kind: z5.enum(MEMORY_KINDS)
|
|
2216
|
+
kind: z5.enum(MEMORY_KINDS),
|
|
2217
|
+
evidenceMessageIndices: z5.array(z5.number().int().nonnegative()).min(1).max(10)
|
|
2167
2218
|
}).strict().transform(parseExtractedMemory)
|
|
2168
2219
|
);
|
|
2169
|
-
function
|
|
2170
|
-
|
|
2171
|
-
|
|
2220
|
+
function isRunActorInstruction(entry) {
|
|
2221
|
+
return entry.type === "message" && entry.role === "user" && entry.provenance?.authority === "instruction" && entry.isRunActor === true;
|
|
2222
|
+
}
|
|
2223
|
+
function isConversationEvidence(entry) {
|
|
2224
|
+
if (entry.type === "toolResult") {
|
|
2225
|
+
return entry.isError === false && Boolean(entry.text?.trim());
|
|
2172
2226
|
}
|
|
2173
|
-
|
|
2227
|
+
if (entry.type === "message" && entry.role === "user" && entry.provenance?.authority === "instruction" && entry.isRunActor === false) {
|
|
2228
|
+
return Boolean(entry.provenance.actor);
|
|
2229
|
+
}
|
|
2230
|
+
return entry.type === "message" && entry.role === "user" && entry.provenance?.authority === "context";
|
|
2174
2231
|
}
|
|
2175
|
-
function
|
|
2176
|
-
|
|
2232
|
+
function citedEntries(indices, transcript) {
|
|
2233
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2234
|
+
const entries = [];
|
|
2235
|
+
for (const index2 of indices) {
|
|
2236
|
+
if (seen.has(index2)) {
|
|
2237
|
+
continue;
|
|
2238
|
+
}
|
|
2239
|
+
seen.add(index2);
|
|
2240
|
+
const entry = transcript[index2];
|
|
2241
|
+
if (!entry) {
|
|
2242
|
+
return { valid: false, entries: [] };
|
|
2243
|
+
}
|
|
2244
|
+
entries.push(entry);
|
|
2245
|
+
}
|
|
2246
|
+
return { valid: entries.length > 0, entries };
|
|
2177
2247
|
}
|
|
2178
|
-
function
|
|
2248
|
+
function routeExtractedMemory(memory, transcript, run) {
|
|
2249
|
+
const cited = citedEntries(memory.evidenceMessageIndices, transcript);
|
|
2250
|
+
if (!cited.valid) {
|
|
2251
|
+
return "drop";
|
|
2252
|
+
}
|
|
2253
|
+
if (memory.kind === "preference") {
|
|
2254
|
+
const exactlyOneRunActor = Boolean(run.actor) && run.actors.length === 1;
|
|
2255
|
+
if (!exactlyOneRunActor) {
|
|
2256
|
+
return "drop";
|
|
2257
|
+
}
|
|
2258
|
+
return cited.entries.every(isRunActorInstruction) ? "personal" : "drop";
|
|
2259
|
+
}
|
|
2260
|
+
return cited.entries.every(
|
|
2261
|
+
(entry) => isRunActorInstruction(entry) || isConversationEvidence(entry)
|
|
2262
|
+
) ? "conversation" : "drop";
|
|
2263
|
+
}
|
|
2264
|
+
function memoryIdempotencySuffix(memory, target) {
|
|
2265
|
+
return createHash2("sha256").update(target).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);
|
|
2266
|
+
}
|
|
2267
|
+
function passiveInput(sessionId, memory, sourceKey2, target) {
|
|
2179
2268
|
return {
|
|
2180
2269
|
content: memory.content,
|
|
2181
|
-
idempotencyKey: `session:${sourceKey2}:${sessionId}:${memoryIdempotencySuffix(memory)}`,
|
|
2270
|
+
idempotencyKey: `session:${sourceKey2}:${sessionId}:${memoryIdempotencySuffix(memory, target)}`,
|
|
2182
2271
|
kind: memory.kind,
|
|
2183
2272
|
...memory.expiresAtMs !== null ? { expiresAtMs: memory.expiresAtMs } : {}
|
|
2184
2273
|
};
|
|
@@ -2187,7 +2276,11 @@ async function getTaskMemories(context, extract) {
|
|
|
2187
2276
|
const cacheKey = `memory-extraction:${context.id}`;
|
|
2188
2277
|
const cached = await context.state.get(cacheKey);
|
|
2189
2278
|
if (cached !== void 0) {
|
|
2190
|
-
|
|
2279
|
+
const parsed = extractedMemoryCacheSchema.safeParse(cached);
|
|
2280
|
+
if (parsed.success) {
|
|
2281
|
+
return parsed.data;
|
|
2282
|
+
}
|
|
2283
|
+
await context.state.delete(cacheKey);
|
|
2191
2284
|
}
|
|
2192
2285
|
const memories = await extract();
|
|
2193
2286
|
if (memories.length > 0) {
|
|
@@ -2216,7 +2309,7 @@ async function processMemorySession(context) {
|
|
|
2216
2309
|
}
|
|
2217
2310
|
const runtimeContext = memoryRuntimeContextSchema.parse({
|
|
2218
2311
|
conversationId: run.conversationId,
|
|
2219
|
-
...run.
|
|
2312
|
+
...run.actor ? { actor: run.actor } : {},
|
|
2220
2313
|
source: run.source
|
|
2221
2314
|
});
|
|
2222
2315
|
const agent = createMemoryAgent(context.model);
|
|
@@ -2234,6 +2327,7 @@ async function processMemorySession(context) {
|
|
|
2234
2327
|
existingMemories: existingMemories.map((memory) => ({
|
|
2235
2328
|
content: memory.content
|
|
2236
2329
|
})),
|
|
2330
|
+
actors: run.actors,
|
|
2237
2331
|
transcript,
|
|
2238
2332
|
runtimeContext
|
|
2239
2333
|
});
|
|
@@ -2242,12 +2336,13 @@ async function processMemorySession(context) {
|
|
|
2242
2336
|
return;
|
|
2243
2337
|
}
|
|
2244
2338
|
for (const memory of memories) {
|
|
2245
|
-
const
|
|
2246
|
-
if (
|
|
2247
|
-
await store.createConversationMemory(input);
|
|
2339
|
+
const target = routeExtractedMemory(memory, transcript, run);
|
|
2340
|
+
if (target === "drop") {
|
|
2248
2341
|
continue;
|
|
2249
2342
|
}
|
|
2250
|
-
|
|
2343
|
+
const input = passiveInput(run.runId, memory, sourceKey2, target);
|
|
2344
|
+
if (target === "conversation") {
|
|
2345
|
+
await store.createConversationMemory(input);
|
|
2251
2346
|
continue;
|
|
2252
2347
|
}
|
|
2253
2348
|
await store.createMemory(input);
|
|
@@ -2298,17 +2393,13 @@ async function createMemoryPromptMessages(context) {
|
|
|
2298
2393
|
}
|
|
2299
2394
|
const runtimeContext = memoryRuntimeContextSchema.parse({
|
|
2300
2395
|
...context.conversationId ? { conversationId: context.conversationId } : {},
|
|
2301
|
-
...context.
|
|
2396
|
+
...context.actor ? { actor: context.actor } : {},
|
|
2302
2397
|
source: context.source
|
|
2303
2398
|
});
|
|
2304
|
-
const memories = await createMemoryStore(
|
|
2305
|
-
context.
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
...context.embedder ? { embedder: context.embedder } : {},
|
|
2309
|
-
...context.maxVectorDistance !== void 0 ? { maxVectorDistance: context.maxVectorDistance } : {}
|
|
2310
|
-
}
|
|
2311
|
-
).searchMemories({
|
|
2399
|
+
const memories = await createMemoryStore(context.db, runtimeContext, {
|
|
2400
|
+
...context.embedder ? { embedder: context.embedder } : {},
|
|
2401
|
+
...context.maxVectorDistance !== void 0 ? { maxVectorDistance: context.maxVectorDistance } : {}
|
|
2402
|
+
}).searchMemories({
|
|
2312
2403
|
query: context.text,
|
|
2313
2404
|
limit: DEFAULT_RECALL_LIMIT
|
|
2314
2405
|
});
|
|
@@ -2345,7 +2436,7 @@ function memoryToolContext(ctx) {
|
|
|
2345
2436
|
return {
|
|
2346
2437
|
agent: ctx.agent,
|
|
2347
2438
|
...ctx.conversationId ? { conversationId: ctx.conversationId } : {},
|
|
2348
|
-
...ctx.
|
|
2439
|
+
...ctx.actor ? { actor: ctx.actor } : {},
|
|
2349
2440
|
db: ctx.db,
|
|
2350
2441
|
...ctx.embedder ? { embedder: ctx.embedder } : {},
|
|
2351
2442
|
source: ctx.source,
|
|
@@ -2405,7 +2496,7 @@ function createMemoryPlugin(options = {}) {
|
|
|
2405
2496
|
async userPrompt(ctx) {
|
|
2406
2497
|
return await createMemoryPromptMessages({
|
|
2407
2498
|
...ctx.conversationId ? { conversationId: ctx.conversationId } : {},
|
|
2408
|
-
...ctx.
|
|
2499
|
+
...ctx.actor ? { actor: ctx.actor } : {},
|
|
2409
2500
|
db: ctx.db,
|
|
2410
2501
|
embedder: ctx.embedder,
|
|
2411
2502
|
maxVectorDistance: recallMaxVectorDistance(options),
|