sessionmem 1.0.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +344 -0
  3. package/dist/adapters/capabilities/fallbackTools.js +36 -0
  4. package/dist/adapters/contract/hostAdapterContract.js +1 -0
  5. package/dist/adapters/factory.js +40 -0
  6. package/dist/adapters/generic.js +128 -0
  7. package/dist/adapters/global/antigravity.js +22 -0
  8. package/dist/adapters/global/claudeCode.js +22 -0
  9. package/dist/adapters/global/codex.js +22 -0
  10. package/dist/adapters/global/qcoder.js +22 -0
  11. package/dist/adapters/ide/cline.js +20 -0
  12. package/dist/adapters/ide/cursor.js +28 -0
  13. package/dist/adapters/ide/installer.js +57 -0
  14. package/dist/adapters/ide/windsurf.js +28 -0
  15. package/dist/adapters/tools/ping.js +15 -0
  16. package/dist/cli/commands/config.js +79 -0
  17. package/dist/cli/commands/export.js +28 -0
  18. package/dist/cli/commands/forget.js +28 -0
  19. package/dist/cli/commands/import.js +112 -0
  20. package/dist/cli/commands/install.js +57 -0
  21. package/dist/cli/commands/list.js +13 -0
  22. package/dist/cli/commands/ping.js +12 -0
  23. package/dist/cli/commands/redactScan.js +40 -0
  24. package/dist/cli/commands/retention.js +54 -0
  25. package/dist/cli/commands/run.js +26 -0
  26. package/dist/cli/commands/search.js +29 -0
  27. package/dist/cli/commands/show.js +15 -0
  28. package/dist/cli/commands/stats.js +46 -0
  29. package/dist/cli/commands/sync.js +118 -0
  30. package/dist/cli/commands/team.js +96 -0
  31. package/dist/cli/commands/uninstall.js +30 -0
  32. package/dist/cli/context.js +69 -0
  33. package/dist/cli/index.js +147 -0
  34. package/dist/cli/output.js +37 -0
  35. package/dist/core/api/contracts.js +263 -0
  36. package/dist/core/api/errors.js +29 -0
  37. package/dist/core/api/localOnlyPolicy.js +29 -0
  38. package/dist/core/api/memoryCoreService.js +595 -0
  39. package/dist/core/api/sessionLifecycleService.js +289 -0
  40. package/dist/core/config/policyConfig.js +131 -0
  41. package/dist/core/embed/deterministicEmbed.js +31 -0
  42. package/dist/core/embed/embeddingVersion.js +1 -0
  43. package/dist/core/embed/reembedPolicy.js +9 -0
  44. package/dist/core/embed/textNormalize.js +12 -0
  45. package/dist/core/injection/formatStartupInjection.js +97 -0
  46. package/dist/core/injection/tokenBudget.js +38 -0
  47. package/dist/core/retrieve/decay.js +15 -0
  48. package/dist/core/retrieve/importance.js +6 -0
  49. package/dist/core/retrieve/recencyBands.js +18 -0
  50. package/dist/core/retrieve/retrieveMemories.js +83 -0
  51. package/dist/core/retrieve/score.js +25 -0
  52. package/dist/core/schema/migrations/001_initial.sql +25 -0
  53. package/dist/core/schema/migrations/002_indexes.sql +18 -0
  54. package/dist/core/schema/migrations/003_summarization_failures.sql +14 -0
  55. package/dist/core/schema/migrations/004_memory_feedback.sql +12 -0
  56. package/dist/core/schema/migrations/005_team_provenance.sql +9 -0
  57. package/dist/core/schema/runMigrations.js +38 -0
  58. package/dist/core/session.js +4 -0
  59. package/dist/core/storage/db.js +8 -0
  60. package/dist/core/storage/memoryFeedbackRepo.js +16 -0
  61. package/dist/core/storage/memoryRepo.js +179 -0
  62. package/dist/core/storage/memorySearchRepo.js +30 -0
  63. package/dist/core/storage/sessionEventsRepo.js +20 -0
  64. package/dist/core/storage/summarizationFailuresRepo.js +39 -0
  65. package/dist/core/storage/types.js +1 -0
  66. package/dist/core/summarize/cloudSummarizer.js +19 -0
  67. package/dist/core/summarize/localSummarizer.js +31 -0
  68. package/dist/core/summarize/redaction.js +48 -0
  69. package/dist/core/summarize/strategySelector.js +7 -0
  70. package/dist/core/summarize/summaryShape.js +49 -0
  71. package/package.json +48 -0
@@ -0,0 +1,263 @@
1
+ import { z } from "zod";
2
+ export const memorySchema = z.object({
3
+ id: z.string().min(1),
4
+ projectId: z.string().min(1),
5
+ sessionId: z.string().min(1),
6
+ sourceAdapter: z.string().min(1),
7
+ kind: z.string().min(1),
8
+ content: z.string().min(1),
9
+ normalizedContent: z.string().min(1),
10
+ importance: z.number().int().min(1).max(10),
11
+ embedding: z.string().nullable(),
12
+ embeddingDim: z.number().int().nullable(),
13
+ embeddingVersion: z.string().nullable(),
14
+ createdAt: z.string().min(1),
15
+ updatedAt: z.string().min(1),
16
+ });
17
+ export const ingestSessionEventSchema = z.object({
18
+ id: z.string().min(1),
19
+ eventIndex: z.number().int().nonnegative(),
20
+ eventType: z.string().min(1),
21
+ payloadJson: z.string().min(1),
22
+ createdAt: z.string().min(1).optional(),
23
+ });
24
+ export const ingestSessionEventsRequestSchema = z.object({
25
+ projectId: z.string().min(1),
26
+ sessionId: z.string().min(1),
27
+ events: z.array(ingestSessionEventSchema).min(1),
28
+ });
29
+ export const summarizeSessionToMemoryRequestSchema = z.object({
30
+ memoryId: z.string().min(1),
31
+ projectId: z.string().min(1),
32
+ sessionId: z.string().min(1),
33
+ sourceAdapter: z.string().min(1),
34
+ summary: z.string().min(1),
35
+ importance: z.number().int().min(1).max(10),
36
+ });
37
+ export const factModeSchema = z.enum([
38
+ "summary-only",
39
+ "facts-only",
40
+ "summary+facts",
41
+ ]);
42
+ export const handleSessionEndConfigSchema = z.object({
43
+ autoSummarize: z.boolean().default(true),
44
+ minimumEventThreshold: z.number().int().min(1).max(100).default(3),
45
+ summaryTokenCap: z.number().int().min(1).default(300),
46
+ // No `.default()`: omission must be distinguishable from an explicit value so
47
+ // the service layer can fall back to the policy-config redactionEnabled
48
+ // setting (override > config.json > default precedence).
49
+ redactionEnabled: z.boolean().optional(),
50
+ factMode: factModeSchema.default("summary+facts"),
51
+ allowCloudSummarization: z.boolean().default(false),
52
+ anthropicApiKey: z.string().min(1).optional(),
53
+ });
54
+ export const handleSessionEndRequestSchema = z.object({
55
+ projectId: z.string().min(1),
56
+ sessionId: z.string().min(1),
57
+ sourceAdapter: z.string().min(1),
58
+ memoryId: z.string().min(1).optional(),
59
+ config: handleSessionEndConfigSchema.default(() => handleSessionEndConfigSchema.parse({})),
60
+ });
61
+ export const storeMemoryRequestSchema = z.object({
62
+ memoryId: z.string().min(1),
63
+ projectId: z.string().min(1),
64
+ sessionId: z.string().min(1),
65
+ sourceAdapter: z.string().min(1),
66
+ kind: z.string().min(1),
67
+ content: z.string().min(1),
68
+ importance: z.number().int().min(1).max(10),
69
+ // No `.default()`: omission must be distinguishable from an explicit value so
70
+ // the service layer can fall back to the policy-config redactionEnabled
71
+ // setting (override > config.json > default precedence).
72
+ redactionEnabled: z.boolean().optional(),
73
+ });
74
+ export const retrieveMemoriesRequestSchema = z.object({
75
+ projectId: z.string().min(1),
76
+ query: z.string().min(1),
77
+ limit: z.number().int().min(1).max(100).default(20),
78
+ mode: z.enum(["auto", "on-demand"]).default("auto"),
79
+ depth: z.enum(["default", "deep"]).default("default"),
80
+ });
81
+ export const recordMemoryUsedRequestSchema = z.object({
82
+ projectId: z.string().min(1),
83
+ memoryId: z.string().min(1),
84
+ feedbackType: z.enum(["auto_use", "manual"]).default("auto_use"),
85
+ usedAt: z.string().min(1).optional(),
86
+ });
87
+ export const listMemoriesRequestSchema = z.object({
88
+ projectId: z.string().min(1),
89
+ });
90
+ export const getMemoryRequestSchema = z.object({
91
+ projectId: z.string().min(1),
92
+ memoryId: z.string().min(1),
93
+ });
94
+ export const forgetMemoryRequestSchema = z.object({
95
+ projectId: z.string().min(1),
96
+ memoryId: z.string().min(1),
97
+ });
98
+ export const exportMemoriesRequestSchema = z.object({
99
+ projectId: z.string().min(1),
100
+ });
101
+ export const importMemoryRecordSchema = z.object({
102
+ id: z.string().min(1),
103
+ projectId: z.string().min(1),
104
+ sessionId: z.string().min(1),
105
+ sourceAdapter: z.string().min(1),
106
+ kind: z.string().min(1),
107
+ content: z.string().min(1),
108
+ importance: z.number().int().min(1).max(10),
109
+ // OPTIONAL for backward-compat with exports predating team provenance (A3):
110
+ // older exports lack author/originProjectId, so the service stamps the local
111
+ // username when author is absent. `.nullable()` because exportMemories emits
112
+ // `originProjectId: null` (and `author: ""`) for locally-authored rows — a
113
+ // team sync round-trips those exported snapshots verbatim, so the schema must
114
+ // accept the null the DTO carries rather than skip-and-warn every local row.
115
+ author: z.string().nullable().optional(),
116
+ originProjectId: z.string().nullable().optional(),
117
+ createdAt: z.string().min(1).optional(),
118
+ updatedAt: z.string().min(1).optional(),
119
+ });
120
+ export const importMemoriesRequestSchema = z.object({
121
+ projectId: z.string().min(1),
122
+ // No `.default()`: omission must be distinguishable from an explicit value so
123
+ // the service layer can fall back to the policy-config redactionEnabled
124
+ // setting (override > config.json > default precedence).
125
+ redactionEnabled: z.boolean().optional(),
126
+ memories: z.array(importMemoryRecordSchema),
127
+ });
128
+ // Team pull. Mirrors importMemoriesRequestSchema — the pull
129
+ // path reuses importMemoryRecordSchema verbatim (carrying author/originProjectId
130
+ // through) but the service applies MAX-importance last-write-wins, author/origin
131
+ // stamping, and the cross-project skip. redactionEnabled is
132
+ // omitted on real pulls so the service resolves config.json.
133
+ export const pullMemoriesRequestSchema = z.object({
134
+ projectId: z.string().min(1),
135
+ redactionEnabled: z.boolean().optional(),
136
+ memories: z.array(importMemoryRecordSchema),
137
+ });
138
+ export const statsRequestSchema = z.object({
139
+ projectId: z.string().min(1),
140
+ });
141
+ export const pruneMemoriesRequestSchema = z.object({
142
+ projectId: z.string().min(1),
143
+ retentionDays: z.number().int(),
144
+ dryRun: z.boolean().default(true),
145
+ });
146
+ export const pruneMemoriesResponseSchema = z.object({
147
+ ok: z.literal(true),
148
+ deleted: z.number().int().nonnegative(),
149
+ eligible: z.number().int().nonnegative(),
150
+ });
151
+ export const redactExistingRequestSchema = z.object({
152
+ projectId: z.string().min(1),
153
+ apply: z.boolean().default(false),
154
+ });
155
+ export const redactExistingResponseSchema = z.object({
156
+ ok: z.literal(true),
157
+ scanned: z.number().int().nonnegative(),
158
+ matched: z.number().int().nonnegative(),
159
+ updated: z.number().int().nonnegative(),
160
+ // Rows that matched but could not be updated (e.g. deleted
161
+ // concurrently between the initial scan and the apply step). A non-zero
162
+ // skipped count lets the CLI report partial success instead of the whole
163
+ // operation erroring out and discarding prior updates/previews.
164
+ skipped: z.number().int().nonnegative().default(0),
165
+ previews: z.array(z.string()),
166
+ });
167
+ export const operationResultSchema = z.object({
168
+ ok: z.literal(true),
169
+ });
170
+ export const singleMemoryResponseSchema = z.object({
171
+ ok: z.literal(true),
172
+ memory: memorySchema,
173
+ });
174
+ // Dedicated store response: extends the single-memory shape with the
175
+ // warningCodes envelope. getMemory continues to use
176
+ // singleMemoryResponseSchema so its shape is unchanged.
177
+ export const storeMemoryResponseSchema = z.object({
178
+ ok: z.literal(true),
179
+ memory: memorySchema,
180
+ warningCodes: z.array(z.string()),
181
+ });
182
+ export const memoryListResponseSchema = z.object({
183
+ ok: z.literal(true),
184
+ memories: z.array(memorySchema),
185
+ total: z.number().int().nonnegative(),
186
+ });
187
+ export const scoreBreakdownSchema = z.object({
188
+ raw: z.object({
189
+ semantic: z.number(),
190
+ recency: z.number(),
191
+ importance: z.number(),
192
+ }),
193
+ weighted: z.object({
194
+ semantic: z.number(),
195
+ recency: z.number(),
196
+ importance: z.number(),
197
+ }),
198
+ total: z.number(),
199
+ });
200
+ export const retrievedMemorySchema = memorySchema.extend({
201
+ semantic: z.number(),
202
+ score: scoreBreakdownSchema,
203
+ });
204
+ export const retrieveMemoriesResponseSchema = z.object({
205
+ ok: z.literal(true),
206
+ memories: z.array(retrievedMemorySchema),
207
+ total: z.number().int().nonnegative(),
208
+ // Pre-rendered startup-injection block: includes the `author:`
209
+ // prefix annotation for teammate-authored memories, derived via
210
+ // formatStartupInjection() with the local username.
211
+ startupInjection: z.string(),
212
+ });
213
+ export const exportMemoriesResponseSchema = z.object({
214
+ ok: z.literal(true),
215
+ memories: z.array(memorySchema),
216
+ });
217
+ export const statsResponseSchema = z.object({
218
+ ok: z.literal(true),
219
+ totalMemories: z.number().int().nonnegative(),
220
+ totalSessionEvents: z.number().int().nonnegative(),
221
+ });
222
+ export const ingestSessionEventsResponseSchema = z.object({
223
+ ok: z.literal(true),
224
+ ingested: z.number().int().nonnegative(),
225
+ });
226
+ export const summarizeSessionToMemoryResponseSchema = z.object({
227
+ ok: z.literal(true),
228
+ memoryId: z.string().min(1),
229
+ });
230
+ export const handleSessionEndResponseSchema = z.object({
231
+ ok: z.literal(true),
232
+ status: z.enum(["stored", "skipped_threshold", "skipped_disabled", "failed"]),
233
+ usedMode: z.enum(["local", "cloud"]),
234
+ warningCodes: z.array(z.string()),
235
+ warningMessages: z.array(z.string()),
236
+ failureRecordId: z.string().min(1).optional(),
237
+ memoryId: z.string().min(1).optional(),
238
+ });
239
+ export const importMemoriesResponseSchema = z.object({
240
+ ok: z.literal(true),
241
+ imported: z.number().int().nonnegative(),
242
+ // Count of records skipped because their `id` already belongs to a
243
+ // different project's memory. These are never upserted, preventing
244
+ // cross-project overwrite/reassignment via ON CONFLICT(id).
245
+ skippedCrossProject: z.number().int().nonnegative().default(0),
246
+ warningCodes: z.array(z.string()),
247
+ });
248
+ // Team pull response: distinguishes brand-new inserts from updates so the
249
+ // `sync` CLI can render "Pushed N memories, pulled M new + updated K from
250
+ // teammates." skippedCrossProject mirrors the import path.
251
+ export const pullMemoriesResponseSchema = z.object({
252
+ ok: z.literal(true),
253
+ pulledNew: z.number().int().nonnegative(),
254
+ pulledUpdated: z.number().int().nonnegative(),
255
+ skippedCrossProject: z.number().int().nonnegative().default(0),
256
+ warningCodes: z.array(z.string()),
257
+ });
258
+ export const recordMemoryUsedResponseSchema = z.object({
259
+ ok: z.literal(true),
260
+ memoryId: z.string().min(1),
261
+ previousImportance: z.number().int().min(1).max(10),
262
+ newImportance: z.number().int().min(1).max(10),
263
+ });
@@ -0,0 +1,29 @@
1
+ export class DomainError extends Error {
2
+ code;
3
+ details;
4
+ constructor(code, message, details) {
5
+ super(message);
6
+ this.name = "DomainError";
7
+ this.code = code;
8
+ this.details = details;
9
+ }
10
+ }
11
+ export function toErrorEnvelope(error) {
12
+ if (error instanceof DomainError) {
13
+ return {
14
+ code: error.code,
15
+ message: error.message,
16
+ details: error.details,
17
+ };
18
+ }
19
+ if (error instanceof Error) {
20
+ return {
21
+ code: "INTERNAL",
22
+ message: error.message,
23
+ };
24
+ }
25
+ return {
26
+ code: "INTERNAL",
27
+ message: "Unexpected error",
28
+ };
29
+ }
@@ -0,0 +1,29 @@
1
+ import { DomainError } from "./errors.js";
2
+ function isExternalProviderEnabled(config) {
3
+ if (!config.enabled) {
4
+ return false;
5
+ }
6
+ if (config.mode && config.mode !== "local") {
7
+ return true;
8
+ }
9
+ if (config.provider) {
10
+ return config.provider !== "local";
11
+ }
12
+ return true;
13
+ }
14
+ export function assertLocalOnlyPolicy(config) {
15
+ const localOnly = config.localOnly ?? true;
16
+ if (!localOnly) {
17
+ return;
18
+ }
19
+ if (config.allowExternalProviders) {
20
+ return;
21
+ }
22
+ const enabledExternalProviders = Object.entries(config.providers ?? {})
23
+ .filter(([, providerConfig]) => providerConfig)
24
+ .filter(([, providerConfig]) => isExternalProviderEnabled(providerConfig))
25
+ .map(([name]) => name);
26
+ if (enabledExternalProviders.length > 0) {
27
+ throw new DomainError("VALIDATION", `Local-only mode blocks external providers: ${enabledExternalProviders.join(", ")}. Set allowExternalProviders=true for explicit opt-in.`);
28
+ }
29
+ }