@sentry/junior-memory 0.124.1 → 0.124.2
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/api.d.ts +16 -6
- package/dist/index.js +114 -59
- package/dist/index.js.map +1 -1
- package/dist/personal-store.d.ts +22 -11
- package/dist/personal.d.ts +5 -3
- package/dist/user-pages.d.ts +2 -2
- package/package.json +2 -2
- package/src/api.ts +13 -8
- package/src/personal-store.ts +108 -46
- package/src/personal.ts +14 -6
- package/src/user-pages.ts +34 -23
package/dist/personal-store.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { type MemoryDb, type MemoryRecord } from "./store";
|
|
3
3
|
import { type MemoryRuntimeContext } from "./types";
|
|
4
|
+
declare const memoryVisibilitySchema: z.ZodEnum<{
|
|
5
|
+
public: "public";
|
|
6
|
+
private: "private";
|
|
7
|
+
}>;
|
|
4
8
|
declare const personalMemoryCursorSchema: z.ZodObject<{
|
|
5
9
|
createdAtMs: z.ZodNumber;
|
|
6
10
|
id: z.ZodString;
|
|
@@ -21,17 +25,23 @@ declare const personalMemoryPageInputSchema: z.ZodObject<{
|
|
|
21
25
|
explicit: "explicit";
|
|
22
26
|
}>>;
|
|
23
27
|
query: z.ZodOptional<z.ZodString>;
|
|
28
|
+
visibility: z.ZodOptional<z.ZodEnum<{
|
|
29
|
+
public: "public";
|
|
30
|
+
private: "private";
|
|
31
|
+
}>>;
|
|
24
32
|
}, z.core.$strict>;
|
|
25
33
|
export type PersonalMemoryCursor = z.output<typeof personalMemoryCursorSchema>;
|
|
26
34
|
export type PersonalMemoryPageInput = z.output<typeof personalMemoryPageInputSchema>;
|
|
35
|
+
export type MemoryVisibility = z.output<typeof memoryVisibilitySchema>;
|
|
27
36
|
export interface PersonalMemoryPage {
|
|
28
37
|
memories: PersonalMemoryRecord[];
|
|
29
38
|
nextCursor?: PersonalMemoryCursor;
|
|
30
39
|
}
|
|
31
|
-
/** Safe provenance attached to one viewer-
|
|
40
|
+
/** Safe provenance attached to one viewer-visible memory. */
|
|
32
41
|
export type PersonalMemoryRecord = MemoryRecord & {
|
|
33
42
|
origin: "automatic" | "explicit" | "other";
|
|
34
43
|
sourcePlatform: "local" | "slack";
|
|
44
|
+
visibility: MemoryVisibility;
|
|
35
45
|
};
|
|
36
46
|
/** Viewer-scoped active memory totals used by the dashboard. */
|
|
37
47
|
export interface PersonalMemoryStats {
|
|
@@ -41,36 +51,37 @@ export interface PersonalMemoryStats {
|
|
|
41
51
|
embedded: number;
|
|
42
52
|
explicit: number;
|
|
43
53
|
knowledge: number;
|
|
54
|
+
personal: number;
|
|
44
55
|
preference: number;
|
|
45
56
|
procedure: number;
|
|
57
|
+
public: number;
|
|
46
58
|
}
|
|
47
59
|
/** Viewer-scoped memory creation totals for one UTC calendar day. */
|
|
48
60
|
export interface PersonalMemoryDay {
|
|
49
61
|
date: string;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
procedure: number;
|
|
62
|
+
personal: number;
|
|
63
|
+
public: number;
|
|
53
64
|
}
|
|
54
65
|
/** Expected failure when a viewer does not own the requested memory. */
|
|
55
66
|
export declare class PersonalMemoryNotFoundError extends Error {
|
|
56
67
|
constructor();
|
|
57
68
|
}
|
|
58
|
-
/** Viewer-scoped
|
|
69
|
+
/** Viewer-scoped memory operations shared by dashboard and REST. */
|
|
59
70
|
export interface PersonalMemoryCollection {
|
|
60
71
|
/** Archive one exact personal memory owned by a linked actor. */
|
|
61
72
|
archive(id: string): Promise<MemoryRecord>;
|
|
62
|
-
/** Read one exact
|
|
63
|
-
get(id: string): Promise<
|
|
64
|
-
/** List one stable page across every
|
|
73
|
+
/** Read one exact memory visible to a linked actor. */
|
|
74
|
+
get(id: string): Promise<PersonalMemoryRecord>;
|
|
75
|
+
/** List one stable page across every authorized viewer scope. */
|
|
65
76
|
list(input: PersonalMemoryPageInput): Promise<PersonalMemoryPage>;
|
|
66
|
-
/** Summarize active memories across every
|
|
77
|
+
/** Summarize active memories across every authorized viewer scope. */
|
|
67
78
|
stats(): Promise<PersonalMemoryStats>;
|
|
68
|
-
/** Read memory creation history across every
|
|
79
|
+
/** Read memory creation history across every authorized viewer scope. */
|
|
69
80
|
timeline(input: {
|
|
70
81
|
days: number;
|
|
71
82
|
}): Promise<PersonalMemoryDay[]>;
|
|
72
83
|
}
|
|
73
|
-
/** Build storage operations for every
|
|
84
|
+
/** Build storage operations for every memory scope linked to one viewer. */
|
|
74
85
|
export declare function createPersonalMemoryCollection(db: MemoryDb, runtimeContexts: MemoryRuntimeContext[], options?: {
|
|
75
86
|
now?: () => number;
|
|
76
87
|
}): PersonalMemoryCollection;
|
package/dist/personal.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PluginUserPageActor } from "@sentry/junior-plugin-api";
|
|
2
|
-
import { type PersonalMemoryRecord } from "./personal-store";
|
|
2
|
+
import { type MemoryVisibility, type PersonalMemoryRecord } from "./personal-store";
|
|
3
3
|
import type { MemoryDb, MemoryRecord } from "./store";
|
|
4
4
|
import type { MemoryKind } from "./types";
|
|
5
5
|
export interface ViewerMemoryPage {
|
|
@@ -12,15 +12,17 @@ export interface ViewerMemoryPageInput {
|
|
|
12
12
|
limit: number;
|
|
13
13
|
origin?: "automatic" | "explicit";
|
|
14
14
|
query?: string;
|
|
15
|
+
visibility?: MemoryVisibility;
|
|
15
16
|
}
|
|
16
17
|
export declare class InvalidMemoryCursorError extends Error {
|
|
17
18
|
constructor();
|
|
18
19
|
}
|
|
19
20
|
export { PersonalMemoryNotFoundError } from "./personal-store";
|
|
20
|
-
|
|
21
|
+
export type { MemoryVisibility, PersonalMemoryRecord } from "./personal-store";
|
|
22
|
+
/** Build viewer memory operations authorized by a viewer's linked actors. */
|
|
21
23
|
export declare function createViewerMemories(db: MemoryDb, actors: PluginUserPageActor[]): {
|
|
22
24
|
archive(id: string): Promise<MemoryRecord>;
|
|
23
|
-
get(id: string): Promise<
|
|
25
|
+
get(id: string): Promise<PersonalMemoryRecord>;
|
|
24
26
|
list(input: ViewerMemoryPageInput): Promise<ViewerMemoryPage>;
|
|
25
27
|
stats(): Promise<import("./personal-store").PersonalMemoryStats>;
|
|
26
28
|
timeline(input: {
|
package/dist/user-pages.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Project viewer-
|
|
1
|
+
/** Project viewer-visible memories into Junior's core-rendered user page. */
|
|
2
2
|
import type { PluginUserPageDefinition } from "@sentry/junior-plugin-api";
|
|
3
|
-
/** Create the interactive
|
|
3
|
+
/** Create the interactive Memories dashboard page. */
|
|
4
4
|
export declare function createMemoryUserPage(): PluginUserPageDefinition;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-memory",
|
|
3
|
-
"version": "0.124.
|
|
3
|
+
"version": "0.124.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"commander": "^14.0.3",
|
|
28
28
|
"drizzle-orm": "^0.45.2",
|
|
29
29
|
"zod": "^4.4.3",
|
|
30
|
-
"@sentry/junior-plugin-api": "0.124.
|
|
30
|
+
"@sentry/junior-plugin-api": "0.124.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^25.9.1",
|
package/src/api.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Authenticated REST resources for
|
|
2
|
+
* Authenticated REST resources for viewer-visible memories.
|
|
3
3
|
*
|
|
4
4
|
* HTTP identity is one verified viewer. Linked platform actors are resolved
|
|
5
|
-
* behind the route boundary and used only to authorize personal
|
|
5
|
+
* behind the route boundary and used only to authorize personal and public
|
|
6
|
+
* workspace scopes.
|
|
6
7
|
*/
|
|
7
8
|
import { z } from "zod";
|
|
8
9
|
import {
|
|
@@ -11,11 +12,12 @@ import {
|
|
|
11
12
|
type PluginRouteApp,
|
|
12
13
|
type PluginUserPageActor,
|
|
13
14
|
} from "@sentry/junior-plugin-api";
|
|
14
|
-
import type { MemoryDb
|
|
15
|
+
import type { MemoryDb } from "./store";
|
|
15
16
|
import {
|
|
16
17
|
createViewerMemories,
|
|
17
18
|
InvalidMemoryCursorError,
|
|
18
19
|
PersonalMemoryNotFoundError,
|
|
20
|
+
type PersonalMemoryRecord,
|
|
19
21
|
} from "./personal";
|
|
20
22
|
|
|
21
23
|
export const memoryApiSchema = z
|
|
@@ -26,6 +28,7 @@ export const memoryApiSchema = z
|
|
|
26
28
|
id: z.string().min(1),
|
|
27
29
|
kind: z.enum(["preference", "procedure", "knowledge"]),
|
|
28
30
|
observedAt: z.iso.datetime(),
|
|
31
|
+
visibility: z.enum(["private", "public"]),
|
|
29
32
|
})
|
|
30
33
|
.strict();
|
|
31
34
|
|
|
@@ -39,9 +42,8 @@ export const memoryListResponseSchema = z
|
|
|
39
42
|
const memoryDashboardDaySchema = z
|
|
40
43
|
.object({
|
|
41
44
|
date: z.iso.date(),
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
procedure: z.number().int().min(0),
|
|
45
|
+
personal: z.number().int().min(0),
|
|
46
|
+
public: z.number().int().min(0),
|
|
45
47
|
})
|
|
46
48
|
.strict();
|
|
47
49
|
|
|
@@ -57,8 +59,10 @@ export const memoryDashboardResponseSchema = z
|
|
|
57
59
|
embedded: z.number().int().min(0),
|
|
58
60
|
explicit: z.number().int().min(0),
|
|
59
61
|
knowledge: z.number().int().min(0),
|
|
62
|
+
personal: z.number().int().min(0),
|
|
60
63
|
preference: z.number().int().min(0),
|
|
61
64
|
procedure: z.number().int().min(0),
|
|
65
|
+
public: z.number().int().min(0),
|
|
62
66
|
})
|
|
63
67
|
.strict(),
|
|
64
68
|
})
|
|
@@ -90,7 +94,7 @@ function json(body: unknown, status = 200): Response {
|
|
|
90
94
|
});
|
|
91
95
|
}
|
|
92
96
|
|
|
93
|
-
function apiMemory(memory:
|
|
97
|
+
function apiMemory(memory: PersonalMemoryRecord): z.output<typeof memoryApiSchema> {
|
|
94
98
|
return {
|
|
95
99
|
content: memory.content,
|
|
96
100
|
createdAt: new Date(memory.createdAtMs).toISOString(),
|
|
@@ -100,6 +104,7 @@ function apiMemory(memory: MemoryRecord): z.output<typeof memoryApiSchema> {
|
|
|
100
104
|
id: memory.id,
|
|
101
105
|
kind: memory.kind,
|
|
102
106
|
observedAt: new Date(memory.observedAtMs).toISOString(),
|
|
107
|
+
visibility: memory.visibility,
|
|
103
108
|
};
|
|
104
109
|
}
|
|
105
110
|
|
|
@@ -113,7 +118,7 @@ function viewerEmail(
|
|
|
113
118
|
return parsed.data.auth.user.email?.trim().toLowerCase() || undefined;
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
/** Create the authenticated
|
|
121
|
+
/** Create the authenticated viewer-memory REST app. */
|
|
117
122
|
export function createMemoryApi(options: MemoryApiOptions): PluginRouteApp {
|
|
118
123
|
return {
|
|
119
124
|
async fetch(request, context) {
|
package/src/personal-store.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SQL operations over
|
|
2
|
+
* SQL operations over memories visible to one authenticated viewer.
|
|
3
3
|
*
|
|
4
4
|
* A viewer may resolve to several runtime actors. This store combines their
|
|
5
|
-
* personal scopes
|
|
5
|
+
* personal scopes with authorized public workspace conversation scopes.
|
|
6
6
|
*/
|
|
7
7
|
import { and, asc, desc, eq, gt, ilike, like, lt, or, sql } from "drizzle-orm";
|
|
8
8
|
import { z } from "zod";
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { MEMORY_KINDS, type MemoryRuntimeContext } from "./types";
|
|
19
19
|
|
|
20
20
|
const nonEmptyStringSchema = z.string().min(1);
|
|
21
|
+
const memoryVisibilitySchema = z.enum(["private", "public"]);
|
|
21
22
|
const personalMemoryCursorSchema = z
|
|
22
23
|
.object({
|
|
23
24
|
createdAtMs: z.number().finite(),
|
|
@@ -31,6 +32,7 @@ const personalMemoryPageInputSchema = z
|
|
|
31
32
|
limit: z.number().int().min(1).max(50),
|
|
32
33
|
origin: z.enum(["automatic", "explicit"]).optional(),
|
|
33
34
|
query: z.string().max(200).optional(),
|
|
35
|
+
visibility: memoryVisibilitySchema.optional(),
|
|
34
36
|
})
|
|
35
37
|
.strict();
|
|
36
38
|
const personalMemoryTimelineInputSchema = z
|
|
@@ -46,15 +48,18 @@ export type PersonalMemoryPageInput = z.output<
|
|
|
46
48
|
typeof personalMemoryPageInputSchema
|
|
47
49
|
>;
|
|
48
50
|
|
|
51
|
+
export type MemoryVisibility = z.output<typeof memoryVisibilitySchema>;
|
|
52
|
+
|
|
49
53
|
export interface PersonalMemoryPage {
|
|
50
54
|
memories: PersonalMemoryRecord[];
|
|
51
55
|
nextCursor?: PersonalMemoryCursor;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
/** Safe provenance attached to one viewer-
|
|
58
|
+
/** Safe provenance attached to one viewer-visible memory. */
|
|
55
59
|
export type PersonalMemoryRecord = MemoryRecord & {
|
|
56
60
|
origin: "automatic" | "explicit" | "other";
|
|
57
61
|
sourcePlatform: "local" | "slack";
|
|
62
|
+
visibility: MemoryVisibility;
|
|
58
63
|
};
|
|
59
64
|
|
|
60
65
|
/** Viewer-scoped active memory totals used by the dashboard. */
|
|
@@ -65,16 +70,17 @@ export interface PersonalMemoryStats {
|
|
|
65
70
|
embedded: number;
|
|
66
71
|
explicit: number;
|
|
67
72
|
knowledge: number;
|
|
73
|
+
personal: number;
|
|
68
74
|
preference: number;
|
|
69
75
|
procedure: number;
|
|
76
|
+
public: number;
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
/** Viewer-scoped memory creation totals for one UTC calendar day. */
|
|
73
80
|
export interface PersonalMemoryDay {
|
|
74
81
|
date: string;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
procedure: number;
|
|
82
|
+
personal: number;
|
|
83
|
+
public: number;
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
/** Expected failure when a viewer does not own the requested memory. */
|
|
@@ -85,17 +91,17 @@ export class PersonalMemoryNotFoundError extends Error {
|
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
/** Viewer-scoped
|
|
94
|
+
/** Viewer-scoped memory operations shared by dashboard and REST. */
|
|
89
95
|
export interface PersonalMemoryCollection {
|
|
90
96
|
/** Archive one exact personal memory owned by a linked actor. */
|
|
91
97
|
archive(id: string): Promise<MemoryRecord>;
|
|
92
|
-
/** Read one exact
|
|
93
|
-
get(id: string): Promise<
|
|
94
|
-
/** List one stable page across every
|
|
98
|
+
/** Read one exact memory visible to a linked actor. */
|
|
99
|
+
get(id: string): Promise<PersonalMemoryRecord>;
|
|
100
|
+
/** List one stable page across every authorized viewer scope. */
|
|
95
101
|
list(input: PersonalMemoryPageInput): Promise<PersonalMemoryPage>;
|
|
96
|
-
/** Summarize active memories across every
|
|
102
|
+
/** Summarize active memories across every authorized viewer scope. */
|
|
97
103
|
stats(): Promise<PersonalMemoryStats>;
|
|
98
|
-
/** Read memory creation history across every
|
|
104
|
+
/** Read memory creation history across every authorized viewer scope. */
|
|
99
105
|
timeline(input: { days: number }): Promise<PersonalMemoryDay[]>;
|
|
100
106
|
}
|
|
101
107
|
|
|
@@ -112,7 +118,30 @@ function personalScopes(
|
|
|
112
118
|
];
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
|
|
121
|
+
/** Public workspace conversation scopes authorized by the viewer's Slack teams. */
|
|
122
|
+
function publicWorkspaceScopes(
|
|
123
|
+
runtimeContexts: MemoryRuntimeContext[],
|
|
124
|
+
): ResolvedMemoryScope[] {
|
|
125
|
+
return [
|
|
126
|
+
...new Map(
|
|
127
|
+
runtimeContexts.flatMap((context) => {
|
|
128
|
+
if (context.source.platform !== "slack" || !context.actor) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
if (context.actor.platform !== "slack") {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
const scope: ResolvedMemoryScope = {
|
|
135
|
+
scope: "conversation",
|
|
136
|
+
scopeKey: `slack:${context.actor.teamId}`,
|
|
137
|
+
};
|
|
138
|
+
return [[`${scope.scope}:${scope.scopeKey}`, scope] as const];
|
|
139
|
+
}),
|
|
140
|
+
).values(),
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function scopePredicate(scopes: ResolvedMemoryScope[]) {
|
|
116
145
|
if (scopes.length === 0) return undefined;
|
|
117
146
|
return or(
|
|
118
147
|
...scopes.map((scope) =>
|
|
@@ -148,30 +177,67 @@ function memoryOrigin(
|
|
|
148
177
|
return "other";
|
|
149
178
|
}
|
|
150
179
|
|
|
180
|
+
function memoryVisibility(
|
|
181
|
+
scope: MemoryRecord["scope"],
|
|
182
|
+
): PersonalMemoryRecord["visibility"] {
|
|
183
|
+
return scope === "personal" ? "private" : "public";
|
|
184
|
+
}
|
|
185
|
+
|
|
151
186
|
function personalMemoryRecord(
|
|
152
187
|
row: typeof juniorMemoryMemories.$inferSelect,
|
|
153
188
|
): PersonalMemoryRecord {
|
|
189
|
+
const memory = parseMemoryRow(row);
|
|
154
190
|
return {
|
|
155
|
-
...
|
|
191
|
+
...memory,
|
|
156
192
|
origin: memoryOrigin(row.idempotencyKey),
|
|
157
193
|
sourcePlatform: row.sourcePlatform,
|
|
194
|
+
visibility: memoryVisibility(memory.scope),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function emptyStats(): PersonalMemoryStats {
|
|
199
|
+
return {
|
|
200
|
+
active: 0,
|
|
201
|
+
automatic: 0,
|
|
202
|
+
createdThirtyDays: 0,
|
|
203
|
+
embedded: 0,
|
|
204
|
+
explicit: 0,
|
|
205
|
+
knowledge: 0,
|
|
206
|
+
personal: 0,
|
|
207
|
+
preference: 0,
|
|
208
|
+
procedure: 0,
|
|
209
|
+
public: 0,
|
|
158
210
|
};
|
|
159
211
|
}
|
|
160
212
|
|
|
161
|
-
/** Build storage operations for every
|
|
213
|
+
/** Build storage operations for every memory scope linked to one viewer. */
|
|
162
214
|
export function createPersonalMemoryCollection(
|
|
163
215
|
db: MemoryDb,
|
|
164
216
|
runtimeContexts: MemoryRuntimeContext[],
|
|
165
217
|
options: { now?: () => number } = {},
|
|
166
218
|
): PersonalMemoryCollection {
|
|
167
|
-
const
|
|
219
|
+
const privateScopes = personalScopes(runtimeContexts);
|
|
220
|
+
const publicScopes = publicWorkspaceScopes(runtimeContexts);
|
|
221
|
+
const allScopes = [...privateScopes, ...publicScopes];
|
|
168
222
|
const getNowMs = () => options.now?.() ?? Date.now();
|
|
169
223
|
|
|
224
|
+
function scopesForVisibility(
|
|
225
|
+
visibility: MemoryVisibility | undefined,
|
|
226
|
+
): ResolvedMemoryScope[] {
|
|
227
|
+
if (visibility === "private") return privateScopes;
|
|
228
|
+
if (visibility === "public") return publicScopes;
|
|
229
|
+
return allScopes;
|
|
230
|
+
}
|
|
231
|
+
|
|
170
232
|
return {
|
|
171
233
|
async archive(id) {
|
|
172
234
|
const memoryId = nonEmptyStringSchema.parse(id);
|
|
173
235
|
const nowMs = getNowMs();
|
|
174
|
-
|
|
236
|
+
// Forget is personal-only; public workspace memories stay shared.
|
|
237
|
+
const predicate = activeVisiblePredicate({
|
|
238
|
+
nowMs,
|
|
239
|
+
scopes: privateScopes,
|
|
240
|
+
});
|
|
175
241
|
if (!predicate) {
|
|
176
242
|
throw new PersonalMemoryNotFoundError();
|
|
177
243
|
}
|
|
@@ -195,7 +261,7 @@ export function createPersonalMemoryCollection(
|
|
|
195
261
|
async get(id) {
|
|
196
262
|
const memoryId = nonEmptyStringSchema.parse(id);
|
|
197
263
|
const nowMs = getNowMs();
|
|
198
|
-
const predicate = activeVisiblePredicate({ nowMs, scopes });
|
|
264
|
+
const predicate = activeVisiblePredicate({ nowMs, scopes: allScopes });
|
|
199
265
|
if (!predicate) {
|
|
200
266
|
throw new PersonalMemoryNotFoundError();
|
|
201
267
|
}
|
|
@@ -207,12 +273,13 @@ export function createPersonalMemoryCollection(
|
|
|
207
273
|
if (!rows[0]) {
|
|
208
274
|
throw new PersonalMemoryNotFoundError();
|
|
209
275
|
}
|
|
210
|
-
return
|
|
276
|
+
return personalMemoryRecord(rows[0]);
|
|
211
277
|
},
|
|
212
278
|
|
|
213
279
|
async list(input) {
|
|
214
280
|
input = personalMemoryPageInputSchema.parse(input);
|
|
215
281
|
const nowMs = getNowMs();
|
|
282
|
+
const scopes = scopesForVisibility(input.visibility);
|
|
216
283
|
await archiveExpiredMemoryBatch({ db, nowMs, scopes });
|
|
217
284
|
const active = activeVisiblePredicate({ nowMs, scopes });
|
|
218
285
|
if (!active) {
|
|
@@ -275,19 +342,10 @@ export function createPersonalMemoryCollection(
|
|
|
275
342
|
|
|
276
343
|
async stats() {
|
|
277
344
|
const nowMs = getNowMs();
|
|
278
|
-
await archiveExpiredMemoryBatch({ db, nowMs, scopes });
|
|
279
|
-
const active = activeVisiblePredicate({ nowMs, scopes });
|
|
345
|
+
await archiveExpiredMemoryBatch({ db, nowMs, scopes: allScopes });
|
|
346
|
+
const active = activeVisiblePredicate({ nowMs, scopes: allScopes });
|
|
280
347
|
if (!active) {
|
|
281
|
-
return
|
|
282
|
-
active: 0,
|
|
283
|
-
automatic: 0,
|
|
284
|
-
createdThirtyDays: 0,
|
|
285
|
-
embedded: 0,
|
|
286
|
-
explicit: 0,
|
|
287
|
-
knowledge: 0,
|
|
288
|
-
preference: 0,
|
|
289
|
-
procedure: 0,
|
|
290
|
-
};
|
|
348
|
+
return emptyStats();
|
|
291
349
|
}
|
|
292
350
|
const [counts] = await db
|
|
293
351
|
.select({
|
|
@@ -312,6 +370,10 @@ export function createPersonalMemoryCollection(
|
|
|
312
370
|
sql<number>`count(*) filter (where ${juniorMemoryMemories.kind} = 'knowledge')`.mapWith(
|
|
313
371
|
Number,
|
|
314
372
|
),
|
|
373
|
+
personal:
|
|
374
|
+
sql<number>`count(*) filter (where ${juniorMemoryMemories.scope} = 'personal')`.mapWith(
|
|
375
|
+
Number,
|
|
376
|
+
),
|
|
315
377
|
preference:
|
|
316
378
|
sql<number>`count(*) filter (where ${juniorMemoryMemories.kind} = 'preference')`.mapWith(
|
|
317
379
|
Number,
|
|
@@ -320,6 +382,10 @@ export function createPersonalMemoryCollection(
|
|
|
320
382
|
sql<number>`count(*) filter (where ${juniorMemoryMemories.kind} = 'procedure')`.mapWith(
|
|
321
383
|
Number,
|
|
322
384
|
),
|
|
385
|
+
public:
|
|
386
|
+
sql<number>`count(*) filter (where ${juniorMemoryMemories.scope} = 'conversation')`.mapWith(
|
|
387
|
+
Number,
|
|
388
|
+
),
|
|
323
389
|
})
|
|
324
390
|
.from(juniorMemoryMemories)
|
|
325
391
|
.leftJoin(
|
|
@@ -334,8 +400,10 @@ export function createPersonalMemoryCollection(
|
|
|
334
400
|
embedded: counts?.embedded ?? 0,
|
|
335
401
|
explicit: counts?.explicit ?? 0,
|
|
336
402
|
knowledge: counts?.knowledge ?? 0,
|
|
403
|
+
personal: counts?.personal ?? 0,
|
|
337
404
|
preference: counts?.preference ?? 0,
|
|
338
405
|
procedure: counts?.procedure ?? 0,
|
|
406
|
+
public: counts?.public ?? 0,
|
|
339
407
|
};
|
|
340
408
|
},
|
|
341
409
|
|
|
@@ -343,13 +411,12 @@ export function createPersonalMemoryCollection(
|
|
|
343
411
|
input = personalMemoryTimelineInputSchema.parse(input);
|
|
344
412
|
const todayMs = Date.parse(`${utcDate(getNowMs())}T00:00:00.000Z`);
|
|
345
413
|
const startMs = todayMs - (input.days - 1) * DAY_MS;
|
|
346
|
-
const ownership =
|
|
414
|
+
const ownership = scopePredicate(allScopes);
|
|
347
415
|
if (!ownership) {
|
|
348
416
|
return Array.from({ length: input.days }, (_, index) => ({
|
|
349
417
|
date: utcDate(startMs + index * DAY_MS),
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
procedure: 0,
|
|
418
|
+
personal: 0,
|
|
419
|
+
public: 0,
|
|
353
420
|
}));
|
|
354
421
|
}
|
|
355
422
|
const rows = await db
|
|
@@ -357,16 +424,12 @@ export function createPersonalMemoryCollection(
|
|
|
357
424
|
date: sql<string>`to_char(to_timestamp(${juniorMemoryMemories.createdAtMs} / 1000.0) AT TIME ZONE 'UTC', 'YYYY-MM-DD')`.as(
|
|
358
425
|
"date",
|
|
359
426
|
),
|
|
360
|
-
|
|
361
|
-
sql<number>`count(*) filter (where ${juniorMemoryMemories.
|
|
427
|
+
personal:
|
|
428
|
+
sql<number>`count(*) filter (where ${juniorMemoryMemories.scope} = 'personal')`.mapWith(
|
|
362
429
|
Number,
|
|
363
430
|
),
|
|
364
|
-
|
|
365
|
-
sql<number>`count(*) filter (where ${juniorMemoryMemories.
|
|
366
|
-
Number,
|
|
367
|
-
),
|
|
368
|
-
procedure:
|
|
369
|
-
sql<number>`count(*) filter (where ${juniorMemoryMemories.kind} = 'procedure')`.mapWith(
|
|
431
|
+
public:
|
|
432
|
+
sql<number>`count(*) filter (where ${juniorMemoryMemories.scope} = 'conversation')`.mapWith(
|
|
370
433
|
Number,
|
|
371
434
|
),
|
|
372
435
|
})
|
|
@@ -383,9 +446,8 @@ export function createPersonalMemoryCollection(
|
|
|
383
446
|
const row = byDate.get(date);
|
|
384
447
|
return {
|
|
385
448
|
date,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
procedure: row?.procedure ?? 0,
|
|
449
|
+
personal: row?.personal ?? 0,
|
|
450
|
+
public: row?.public ?? 0,
|
|
389
451
|
};
|
|
390
452
|
});
|
|
391
453
|
},
|
package/src/personal.ts
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* Authenticated-viewer memory access shared by REST and dashboard projections.
|
|
3
3
|
*
|
|
4
4
|
* Viewer identity may resolve to multiple platform actors. This module keeps
|
|
5
|
-
* that federation behind one
|
|
5
|
+
* that federation behind one viewer-memory collection spanning personal and
|
|
6
|
+
* authorized public workspace scopes.
|
|
6
7
|
*/
|
|
7
8
|
import { z } from "zod";
|
|
8
9
|
import type { PluginUserPageActor } from "@sentry/junior-plugin-api";
|
|
9
10
|
import {
|
|
10
11
|
createPersonalMemoryCollection,
|
|
12
|
+
type MemoryVisibility,
|
|
11
13
|
type PersonalMemoryRecord,
|
|
12
14
|
} from "./personal-store";
|
|
13
15
|
import type { MemoryDb, MemoryRecord } from "./store";
|
|
@@ -21,6 +23,7 @@ const cursorSchema = z
|
|
|
21
23
|
origin: z.enum(["automatic", "explicit"]).optional(),
|
|
22
24
|
query: z.string().max(200).optional(),
|
|
23
25
|
version: z.literal(1),
|
|
26
|
+
visibility: z.enum(["private", "public"]).optional(),
|
|
24
27
|
})
|
|
25
28
|
.strict();
|
|
26
29
|
|
|
@@ -35,6 +38,7 @@ export interface ViewerMemoryPageInput {
|
|
|
35
38
|
limit: number;
|
|
36
39
|
origin?: "automatic" | "explicit";
|
|
37
40
|
query?: string;
|
|
41
|
+
visibility?: MemoryVisibility;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
export class InvalidMemoryCursorError extends Error {
|
|
@@ -45,6 +49,7 @@ export class InvalidMemoryCursorError extends Error {
|
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
export { PersonalMemoryNotFoundError } from "./personal-store";
|
|
52
|
+
export type { MemoryVisibility, PersonalMemoryRecord } from "./personal-store";
|
|
48
53
|
|
|
49
54
|
function runtimeContext(actor: PluginUserPageActor): MemoryRuntimeContext {
|
|
50
55
|
if (actor.platform === "slack") {
|
|
@@ -70,7 +75,7 @@ function runtimeContext(actor: PluginUserPageActor): MemoryRuntimeContext {
|
|
|
70
75
|
|
|
71
76
|
function decodeCursor(
|
|
72
77
|
value: string | undefined,
|
|
73
|
-
input: Pick<ViewerMemoryPageInput, "kind" | "origin" | "query">,
|
|
78
|
+
input: Pick<ViewerMemoryPageInput, "kind" | "origin" | "query" | "visibility">,
|
|
74
79
|
) {
|
|
75
80
|
if (!value) return undefined;
|
|
76
81
|
try {
|
|
@@ -80,7 +85,8 @@ function decodeCursor(
|
|
|
80
85
|
if (
|
|
81
86
|
parsed.query !== input.query ||
|
|
82
87
|
parsed.kind !== input.kind ||
|
|
83
|
-
parsed.origin !== input.origin
|
|
88
|
+
parsed.origin !== input.origin ||
|
|
89
|
+
parsed.visibility !== input.visibility
|
|
84
90
|
) {
|
|
85
91
|
throw new InvalidMemoryCursorError();
|
|
86
92
|
}
|
|
@@ -92,7 +98,7 @@ function decodeCursor(
|
|
|
92
98
|
|
|
93
99
|
function encodeCursor(
|
|
94
100
|
cursor: { createdAtMs: number; id: string },
|
|
95
|
-
input: Pick<ViewerMemoryPageInput, "kind" | "origin" | "query">,
|
|
101
|
+
input: Pick<ViewerMemoryPageInput, "kind" | "origin" | "query" | "visibility">,
|
|
96
102
|
): string {
|
|
97
103
|
return Buffer.from(
|
|
98
104
|
JSON.stringify({
|
|
@@ -100,13 +106,14 @@ function encodeCursor(
|
|
|
100
106
|
...(input.query ? { query: input.query } : {}),
|
|
101
107
|
...(input.kind ? { kind: input.kind } : {}),
|
|
102
108
|
...(input.origin ? { origin: input.origin } : {}),
|
|
109
|
+
...(input.visibility ? { visibility: input.visibility } : {}),
|
|
103
110
|
version: 1,
|
|
104
111
|
}),
|
|
105
112
|
"utf8",
|
|
106
113
|
).toString("base64url");
|
|
107
114
|
}
|
|
108
115
|
|
|
109
|
-
/** Build
|
|
116
|
+
/** Build viewer memory operations authorized by a viewer's linked actors. */
|
|
110
117
|
export function createViewerMemories(
|
|
111
118
|
db: MemoryDb,
|
|
112
119
|
actors: PluginUserPageActor[],
|
|
@@ -119,7 +126,7 @@ export function createViewerMemories(
|
|
|
119
126
|
async archive(id: string): Promise<MemoryRecord> {
|
|
120
127
|
return await collection.archive(id);
|
|
121
128
|
},
|
|
122
|
-
async get(id: string): Promise<
|
|
129
|
+
async get(id: string): Promise<PersonalMemoryRecord> {
|
|
123
130
|
return await collection.get(id);
|
|
124
131
|
},
|
|
125
132
|
async list(input: ViewerMemoryPageInput): Promise<ViewerMemoryPage> {
|
|
@@ -128,6 +135,7 @@ export function createViewerMemories(
|
|
|
128
135
|
...(input.kind ? { kind: input.kind } : {}),
|
|
129
136
|
...(input.origin ? { origin: input.origin } : {}),
|
|
130
137
|
...(query ? { query } : {}),
|
|
138
|
+
...(input.visibility ? { visibility: input.visibility } : {}),
|
|
131
139
|
};
|
|
132
140
|
const page = await collection.list({
|
|
133
141
|
cursor: decodeCursor(input.cursor, filters),
|