@evo-dev/core 0.0.1-alpha → 0.0.1-alpha.10

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 (85) hide show
  1. package/assets/agents/review/code-reviewer/examples.md +1 -1
  2. package/assets/agents/review/code-reviewer/prompt.md +1 -1
  3. package/assets/agents/review/code-reviewer/verification.md +1 -1
  4. package/assets/skills/coding/knowledge-distillation/SKILL.md +251 -0
  5. package/assets/skills/coding/knowledge-distillation/manifest.json +10 -0
  6. package/assets/skills/coding/knowledge-distillation/references/knowledge-distillation-methods.md +126 -0
  7. package/assets/team/agents/code-reviewer.md +48 -0
  8. package/assets/team/agents/docs-maintainer.md +51 -0
  9. package/assets/team/agents/implementation-engineer.md +51 -0
  10. package/assets/team/agents/product-scope-analyst.md +58 -0
  11. package/assets/team/agents/release-engineer.md +55 -0
  12. package/assets/team/agents/security-boundary-reviewer.md +50 -0
  13. package/assets/team/agents/solution-architect.md +51 -0
  14. package/assets/team/agents/verification-engineer.md +51 -0
  15. package/assets/team/team.md +102 -0
  16. package/assets/workflows/rd-bug-fix/WORKFLOW.json +1 -1
  17. package/assets/workflows/rd-code-review/WORKFLOW.json +1 -1
  18. package/assets/workflows/rd-docs-update/WORKFLOW.json +1 -1
  19. package/assets/workflows/rd-feature-implementation/WORKFLOW.json +1 -1
  20. package/assets/workflows/rd-refactor/WORKFLOW.json +1 -1
  21. package/assets/workflows/rd-release-readiness/WORKFLOW.json +1 -1
  22. package/assets/workflows/rd-security-boundary-review/WORKFLOW.json +2 -2
  23. package/assets/workflows/rd-test-generation/WORKFLOW.json +1 -1
  24. package/dist/config/index.js +1115 -81
  25. package/dist/index.js +13796 -2196
  26. package/dist/plugins/index.js +32 -32
  27. package/package.json +5 -1
  28. package/src/agents/index.ts +63 -292
  29. package/src/code-agent-traces/index.ts +520 -0
  30. package/src/config/index.ts +7 -0
  31. package/src/config/paths.ts +30 -0
  32. package/src/config/settings.ts +201 -0
  33. package/src/config/store.ts +152 -0
  34. package/src/daemon/index.ts +462 -40
  35. package/src/evolution/candidates/index.ts +564 -0
  36. package/src/evolution/control/index.ts +20 -0
  37. package/src/evolution/evidence/analysis.ts +533 -0
  38. package/src/evolution/evidence/index.ts +3 -0
  39. package/src/evolution/evidence/session-memory/analysis.ts +281 -0
  40. package/src/evolution/evidence/session-memory/constants.ts +9 -0
  41. package/src/evolution/evidence/session-memory/index.ts +7 -0
  42. package/src/evolution/evidence/session-memory/paths.ts +29 -0
  43. package/src/evolution/evidence/session-memory/policy.ts +39 -0
  44. package/src/evolution/evidence/session-memory/segment.ts +202 -0
  45. package/src/evolution/evidence/session-memory/sensitivity.ts +335 -0
  46. package/src/evolution/evidence/session-memory/state-machine.ts +249 -0
  47. package/src/evolution/evidence/session-memory/storage.ts +379 -0
  48. package/src/evolution/evidence/session-memory/types.ts +221 -0
  49. package/src/evolution/evidence/session-memory/updater.ts +191 -0
  50. package/src/evolution/formatters.ts +169 -0
  51. package/src/evolution/index.ts +16 -0
  52. package/src/evolution/knowledge/index.ts +5427 -0
  53. package/src/evolution/paths.ts +44 -0
  54. package/src/evolution/processor/distillation.ts +518 -0
  55. package/src/evolution/processor/index.ts +3 -0
  56. package/src/evolution/processor/process.ts +528 -0
  57. package/src/{learning → evolution/review}/index.ts +10 -14
  58. package/src/evolution/schema.ts +568 -0
  59. package/src/evolution/shared.ts +758 -0
  60. package/src/evolution/triggers/classification.ts +102 -0
  61. package/src/evolution/triggers/index.ts +295 -0
  62. package/src/hooks/index.ts +652 -376
  63. package/src/index.ts +16 -3
  64. package/src/pack/index.ts +13 -13
  65. package/src/plugins/capabilities.ts +40 -42
  66. package/src/plugins/index.ts +0 -1
  67. package/src/plugins/types.ts +4 -0
  68. package/src/projects/index.ts +453 -0
  69. package/src/protected-zones/index.ts +29 -11
  70. package/src/runtime-logs/index.ts +790 -0
  71. package/src/sync/orchestrator.ts +6 -0
  72. package/src/team/index.ts +3642 -0
  73. package/src/team/mcp.ts +405 -0
  74. package/src/team/prompts.ts +141 -0
  75. package/src/utils/errors.ts +13 -0
  76. package/src/utils/fs.ts +40 -0
  77. package/src/utils/hash.ts +9 -0
  78. package/src/utils/ids.ts +12 -0
  79. package/src/utils/index.ts +7 -0
  80. package/src/utils/parsing.ts +11 -0
  81. package/src/utils/text.ts +18 -0
  82. package/src/utils/time.ts +5 -0
  83. package/src/workflow/index.ts +6 -24
  84. package/src/project/index.ts +0 -507
  85. package/src/task/index.ts +0 -840
@@ -0,0 +1,379 @@
1
+ import { appendFile, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { resolveEvoDevPaths } from "../../../config/paths.ts";
4
+ import { isNotFoundError } from "../../../utils/index.ts";
5
+ import { resolveSessionMemoryPaths } from "./paths.ts";
6
+ import { detectSessionMemorySensitivity, mergeSessionMemorySensitivity } from "./sensitivity.ts";
7
+ import type {
8
+ SessionEvidenceSegmentV1,
9
+ SessionMemoryCursorV1,
10
+ SessionMemoryIndexSegment,
11
+ SessionMemoryPaths,
12
+ SessionMemoryRawEventV1,
13
+ SessionMemorySensitivity,
14
+ SessionMemorySensitivityReason,
15
+ SessionMemoryStateV1,
16
+ } from "./types.ts";
17
+
18
+ export class SessionMemoryEvidenceError extends Error {
19
+ readonly code: "not-found" | "invalid";
20
+
21
+ constructor(code: "not-found" | "invalid", message: string) {
22
+ super(message);
23
+ this.name = "SessionMemoryEvidenceError";
24
+ this.code = code;
25
+ }
26
+ }
27
+
28
+ export async function listSessionEvidenceSegments(input: {
29
+ homeDir: string;
30
+ projectKey?: string;
31
+ }): Promise<SessionEvidenceSegmentV1[]> {
32
+ const rootDir = join(resolveEvoDevPaths(input.homeDir).stateDir, "session-memory");
33
+ const projectDirs =
34
+ input.projectKey === undefined ? await listDirectoryNames(rootDir) : [input.projectKey];
35
+ const segments: SessionEvidenceSegmentV1[] = [];
36
+ for (const projectKey of projectDirs) {
37
+ const paths = resolveSessionMemoryPaths({
38
+ homeDir: input.homeDir,
39
+ projectKey,
40
+ sessionKey: "list",
41
+ });
42
+ for (const sessionKey of await listDirectoryNames(paths.projectDir)) {
43
+ const sessionPaths = resolveSessionMemoryPaths({
44
+ homeDir: input.homeDir,
45
+ projectKey,
46
+ sessionKey,
47
+ });
48
+ for (const entry of await listJsonFiles(sessionPaths.segmentsDir)) {
49
+ try {
50
+ segments.push(parseSessionEvidenceSegment(JSON.parse(await readFile(entry, "utf8"))));
51
+ } catch {
52
+ // Invalid local evidence is omitted from listings and remains available for diagnostics.
53
+ }
54
+ }
55
+ }
56
+ }
57
+ return segments.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
58
+ }
59
+
60
+ export async function listSessionMemoryStates(input: {
61
+ homeDir: string;
62
+ projectKey?: string;
63
+ }): Promise<SessionMemoryStateV1[]> {
64
+ const rootDir = join(resolveEvoDevPaths(input.homeDir).stateDir, "session-memory");
65
+ const projectDirs =
66
+ input.projectKey === undefined ? await listDirectoryNames(rootDir) : [input.projectKey];
67
+ const states: SessionMemoryStateV1[] = [];
68
+ for (const projectKey of projectDirs) {
69
+ const projectDir = join(rootDir, projectKey);
70
+ for (const sessionKey of await listDirectoryNames(projectDir)) {
71
+ try {
72
+ const state = parseSessionMemoryState(
73
+ JSON.parse(await readFile(join(projectDir, sessionKey, "state.json"), "utf8")) as unknown,
74
+ );
75
+ if (state.projectKey === projectKey && state.sessionKey === sessionKey) states.push(state);
76
+ } catch {
77
+ // Invalid local state is omitted from listings and remains available for diagnostics.
78
+ }
79
+ }
80
+ }
81
+ return states.sort(
82
+ (left, right) =>
83
+ right.updatedAt.localeCompare(left.updatedAt) ||
84
+ left.sessionKey.localeCompare(right.sessionKey),
85
+ );
86
+ }
87
+
88
+ export async function readSessionEvidenceSegment(input: {
89
+ homeDir: string;
90
+ projectKey: string;
91
+ sessionKey: string;
92
+ segmentId: string;
93
+ }): Promise<SessionEvidenceSegmentV1> {
94
+ const paths = resolveSessionMemoryPaths(input);
95
+ try {
96
+ const segment = parseSessionEvidenceSegment(
97
+ JSON.parse(await readFile(paths.segmentPath(input.segmentId), "utf8")) as unknown,
98
+ );
99
+ if (
100
+ segment.id !== input.segmentId ||
101
+ segment.projectKey !== input.projectKey ||
102
+ segment.sessionKey !== input.sessionKey
103
+ ) {
104
+ throw new SessionMemoryEvidenceError(
105
+ "invalid",
106
+ "Session evidence identity does not match its storage path.",
107
+ );
108
+ }
109
+ return segment;
110
+ } catch (error) {
111
+ if (error instanceof SessionMemoryEvidenceError) throw error;
112
+ if (isNotFoundError(error)) {
113
+ throw new SessionMemoryEvidenceError("not-found", "Session evidence segment was not found.");
114
+ }
115
+ throw new SessionMemoryEvidenceError("invalid", "Session evidence segment is invalid.");
116
+ }
117
+ }
118
+
119
+ export async function updateSessionEvidenceSegmentLifecycle(input: {
120
+ homeDir: string;
121
+ projectKey: string;
122
+ sessionKey: string;
123
+ segmentId: string;
124
+ status: "distilled" | "ignored";
125
+ consumedByBatchId?: string;
126
+ }): Promise<SessionEvidenceSegmentV1> {
127
+ const segment = await readSessionEvidenceSegment(input);
128
+ const consumedByBatchIds =
129
+ input.consumedByBatchId === undefined
130
+ ? segment.lifecycle.consumedByBatchIds
131
+ : [...new Set([...segment.lifecycle.consumedByBatchIds, input.consumedByBatchId])];
132
+ const updated: SessionEvidenceSegmentV1 = {
133
+ ...segment,
134
+ lifecycle: {
135
+ ...segment.lifecycle,
136
+ status: input.status,
137
+ consumedByBatchIds,
138
+ },
139
+ };
140
+ const paths = resolveSessionMemoryPaths(input);
141
+ await writeJson(paths.segmentPath(input.segmentId), updated);
142
+ return updated;
143
+ }
144
+
145
+ export async function readSessionState(path: string): Promise<SessionMemoryStateV1 | null> {
146
+ try {
147
+ return JSON.parse(await readFile(path, "utf8")) as SessionMemoryStateV1;
148
+ } catch (error) {
149
+ if (isNotFoundError(error)) return null;
150
+ throw error;
151
+ }
152
+ }
153
+
154
+ export async function readSessionCursor(
155
+ path: string,
156
+ sessionKey: string,
157
+ sourcePath: string,
158
+ now: string,
159
+ ): Promise<SessionMemoryCursorV1> {
160
+ try {
161
+ return JSON.parse(await readFile(path, "utf8")) as SessionMemoryCursorV1;
162
+ } catch (error) {
163
+ if (!isNotFoundError(error)) throw error;
164
+ return {
165
+ schemaVersion: 1,
166
+ kind: "session-memory-cursor",
167
+ sessionKey,
168
+ traceRefId: null,
169
+ sourcePath,
170
+ lastCapturedOffset: null,
171
+ lastCapturedLine: null,
172
+ lastCapturedEventId: null,
173
+ updatedAt: now,
174
+ };
175
+ }
176
+ }
177
+
178
+ export async function appendRawEvent(
179
+ path: string,
180
+ event: SessionMemoryRawEventV1,
181
+ ): Promise<{ lineNumber: number }> {
182
+ const existingLineCount = await countJsonlLines(path);
183
+ await mkdir(dirname(path), { recursive: true });
184
+ await appendFile(path, `${JSON.stringify(event)}\n`, "utf8");
185
+ return { lineNumber: existingLineCount + 1 };
186
+ }
187
+
188
+ export async function readLineRange(
189
+ path: string,
190
+ fromLine: number,
191
+ toLine: number,
192
+ ): Promise<{
193
+ content: string;
194
+ truncated: boolean;
195
+ secretsDetected: boolean;
196
+ sensitivity: SessionMemorySensitivity;
197
+ sensitivityReasons: SessionMemorySensitivityReason[];
198
+ redactionApplied: boolean;
199
+ }> {
200
+ const text = await readFile(path, "utf8");
201
+ const lines = text.split("\n").filter((line) => line.trim() !== "");
202
+ const selected = lines.slice(Math.max(0, fromLine - 1), toLine);
203
+ const detections = [];
204
+ let redactionApplied = false;
205
+ for (const line of selected) {
206
+ try {
207
+ const parsed = JSON.parse(line) as Partial<SessionMemoryRawEventV1>;
208
+ redactionApplied ||= parsed.credentialRedactionApplied === true;
209
+ if (parsed.sensitivity !== undefined) {
210
+ detections.push({
211
+ classification: parsed.sensitivity,
212
+ reasons: [...(parsed.sensitivityReasons ?? [])],
213
+ });
214
+ } else if (typeof parsed.rawPayloadJson === "string") {
215
+ try {
216
+ detections.push(
217
+ detectSessionMemorySensitivity(JSON.parse(parsed.rawPayloadJson) as unknown),
218
+ );
219
+ } catch {
220
+ detections.push(detectSessionMemorySensitivity(parsed.rawPayloadJson));
221
+ }
222
+ } else if (parsed.secretsDetected === true) {
223
+ detections.push({
224
+ classification: "credential" as const,
225
+ reasons: ["legacy-sensitive-flag" as const],
226
+ });
227
+ }
228
+ } catch {
229
+ detections.push(detectSessionMemorySensitivity(line));
230
+ }
231
+ }
232
+ const sensitivity = mergeSessionMemorySensitivity(detections);
233
+ return {
234
+ content: selected.join("\n"),
235
+ truncated: false,
236
+ secretsDetected: sensitivity.classification === "credential",
237
+ sensitivity: sensitivity.classification,
238
+ sensitivityReasons: sensitivity.reasons,
239
+ redactionApplied,
240
+ };
241
+ }
242
+
243
+ export async function writeSessionIndex(
244
+ paths: SessionMemoryPaths,
245
+ state: SessionMemoryStateV1,
246
+ segment: SessionEvidenceSegmentV1,
247
+ now: string,
248
+ ): Promise<void> {
249
+ const existing = await readSessionIndex(paths.indexPath);
250
+ const segments = [
251
+ ...existing.segments.filter((item) => item.id !== segment.id),
252
+ {
253
+ id: segment.id,
254
+ reason: segment.reason,
255
+ strength: segment.strength,
256
+ createdAt: segment.createdAt,
257
+ reviewState: segment.lifecycle.reviewState,
258
+ },
259
+ ];
260
+ await writeJson(paths.indexPath, {
261
+ schemaVersion: 1,
262
+ kind: "session-memory-index",
263
+ projectKey: state.projectKey,
264
+ sessionKey: state.sessionKey,
265
+ updatedAt: now,
266
+ lastSegmentId: segment.id,
267
+ segments,
268
+ });
269
+ }
270
+
271
+ export async function writeJson(path: string, value: unknown): Promise<void> {
272
+ await mkdir(dirname(path), { recursive: true });
273
+ await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
274
+ }
275
+
276
+ async function countJsonlLines(path: string): Promise<number> {
277
+ try {
278
+ const text = await readFile(path, "utf8");
279
+ return text.split("\n").filter((line) => line.trim() !== "").length;
280
+ } catch (error) {
281
+ if (isNotFoundError(error)) return 0;
282
+ throw error;
283
+ }
284
+ }
285
+
286
+ async function readSessionIndex(path: string): Promise<{ segments: SessionMemoryIndexSegment[] }> {
287
+ try {
288
+ const parsed = JSON.parse(await readFile(path, "utf8")) as {
289
+ segments?: unknown;
290
+ };
291
+ if (!Array.isArray(parsed.segments)) return { segments: [] };
292
+ return {
293
+ segments: parsed.segments.filter(isSessionIndexSegment),
294
+ };
295
+ } catch (error) {
296
+ if (isNotFoundError(error)) return { segments: [] };
297
+ throw error;
298
+ }
299
+ }
300
+
301
+ function isSessionIndexSegment(value: unknown): value is SessionMemoryIndexSegment {
302
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
303
+ const item = value as Record<string, unknown>;
304
+ return (
305
+ typeof item.id === "string" &&
306
+ typeof item.reason === "string" &&
307
+ typeof item.strength === "string" &&
308
+ typeof item.createdAt === "string" &&
309
+ typeof item.reviewState === "string"
310
+ );
311
+ }
312
+
313
+ function parseSessionEvidenceSegment(value: unknown): SessionEvidenceSegmentV1 {
314
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
315
+ throw new SessionMemoryEvidenceError("invalid", "Session evidence must be an object.");
316
+ }
317
+ const segment = value as Partial<SessionEvidenceSegmentV1>;
318
+ if (
319
+ segment.schemaVersion !== 1 ||
320
+ segment.kind !== "session-evidence-segment" ||
321
+ typeof segment.id !== "string" ||
322
+ typeof segment.projectKey !== "string" ||
323
+ typeof segment.sessionKey !== "string" ||
324
+ typeof segment.createdAt !== "string" ||
325
+ typeof segment.normalized !== "object" ||
326
+ segment.normalized === null ||
327
+ typeof segment.rawExcerpt !== "object" ||
328
+ segment.rawExcerpt === null
329
+ ) {
330
+ throw new SessionMemoryEvidenceError("invalid", "Session evidence fields are invalid.");
331
+ }
332
+ return segment as SessionEvidenceSegmentV1;
333
+ }
334
+
335
+ function parseSessionMemoryState(value: unknown): SessionMemoryStateV1 {
336
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
337
+ throw new SessionMemoryEvidenceError("invalid", "Session Memory state must be an object.");
338
+ }
339
+ const state = value as Partial<SessionMemoryStateV1>;
340
+ if (
341
+ state.schemaVersion !== 1 ||
342
+ state.kind !== "session-memory-state" ||
343
+ typeof state.projectKey !== "string" ||
344
+ typeof state.sessionKey !== "string" ||
345
+ !(state.runId === null || typeof state.runId === "string") ||
346
+ !(state.roleId === null || typeof state.roleId === "string") ||
347
+ typeof state.createdAt !== "string" ||
348
+ typeof state.updatedAt !== "string" ||
349
+ typeof state.counters !== "object" ||
350
+ state.counters === null
351
+ ) {
352
+ throw new SessionMemoryEvidenceError("invalid", "Session Memory state fields are invalid.");
353
+ }
354
+ return state as SessionMemoryStateV1;
355
+ }
356
+
357
+ async function listDirectoryNames(path: string): Promise<string[]> {
358
+ try {
359
+ return (await readdir(path, { withFileTypes: true }))
360
+ .filter((entry) => entry.isDirectory())
361
+ .map((entry) => entry.name)
362
+ .sort();
363
+ } catch (error) {
364
+ if (isNotFoundError(error)) return [];
365
+ throw error;
366
+ }
367
+ }
368
+
369
+ async function listJsonFiles(path: string): Promise<string[]> {
370
+ try {
371
+ return (await readdir(path, { withFileTypes: true }))
372
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
373
+ .map((entry) => join(path, entry.name))
374
+ .sort();
375
+ } catch (error) {
376
+ if (isNotFoundError(error)) return [];
377
+ throw error;
378
+ }
379
+ }
@@ -0,0 +1,221 @@
1
+ export type SessionMemoryTarget = "claude" | "codex" | "unknown";
2
+ export type SessionMemorySegmentReason =
3
+ | "session-memory-init"
4
+ | "session-memory-threshold"
5
+ | "intent-refinement"
6
+ | "user-interruption"
7
+ | "failure-signal"
8
+ | "permission-denied"
9
+ | "verification-after-fix"
10
+ | "explicit-memory-intent";
11
+ export type SessionMemorySignalStrength = "normal" | "strong";
12
+ export type SessionMemorySensitivity = "safe-metadata" | "private-content" | "credential";
13
+ export type SessionMemorySensitivityReason =
14
+ | "credential-field"
15
+ | "credential-shape"
16
+ | "credential-url-parameter"
17
+ | "credential-url-userinfo"
18
+ | "legacy-sensitive-flag"
19
+ | "private-marker"
20
+ | "url";
21
+
22
+ export interface SessionMemoryPolicySnapshot {
23
+ enabled: boolean;
24
+ storeRawSegments: boolean;
25
+ maxRawSegmentBytes: number;
26
+ retentionDays: number;
27
+ minimumMessageTokensToInit: number;
28
+ minimumTokensBetweenUpdate: number;
29
+ toolCallsBetweenUpdates: number;
30
+ }
31
+
32
+ export interface SessionMemorySignal {
33
+ eventId: string;
34
+ eventType: string;
35
+ reason: SessionMemorySegmentReason | "observed";
36
+ strength: SessionMemorySignalStrength;
37
+ occurredAt: string | null;
38
+ summary: string;
39
+ rawEventLine: number;
40
+ }
41
+
42
+ export interface SessionMemorySpan {
43
+ spanId: string;
44
+ status: "open" | "closed" | "interrupted";
45
+ openedAt: string;
46
+ closedAt: string | null;
47
+ openedByEventId: string;
48
+ lastEventId: string;
49
+ userPromptCount: number;
50
+ toolCallCount: number;
51
+ workStarted: boolean;
52
+ tokenEstimate: number;
53
+ }
54
+
55
+ export interface SessionMemoryStateV1 {
56
+ schemaVersion: 1;
57
+ kind: "session-memory-state";
58
+ projectKey: string;
59
+ sessionKey: string;
60
+ target: SessionMemoryTarget;
61
+ runId: string | null;
62
+ roleId: string | null;
63
+ initialized: boolean;
64
+ createdAt: string;
65
+ updatedAt: string;
66
+ counters: {
67
+ messageTokenEstimate: number;
68
+ tokensSinceLastUpdate: number;
69
+ toolCallsSinceLastUpdate: number;
70
+ userPromptCount: number;
71
+ assistantTurnCount: number;
72
+ toolCallCount: number;
73
+ failureSignalCount: number;
74
+ verificationSignalCount: number;
75
+ };
76
+ activeSpan: SessionMemorySpan | null;
77
+ pendingSignals: SessionMemorySignal[];
78
+ lastSegmentId: string | null;
79
+ lastMemoryUpdateAt: string | null;
80
+ policy: SessionMemoryPolicySnapshot;
81
+ }
82
+
83
+ export interface SessionMemoryCursorV1 {
84
+ schemaVersion: 1;
85
+ kind: "session-memory-cursor";
86
+ sessionKey: string;
87
+ traceRefId: string | null;
88
+ sourcePath: string | null;
89
+ lastCapturedOffset: number | null;
90
+ lastCapturedLine: number | null;
91
+ lastCapturedEventId: string | null;
92
+ updatedAt: string;
93
+ }
94
+
95
+ export interface SessionEvidenceSegmentV1 {
96
+ schemaVersion: 1;
97
+ kind: "session-evidence-segment";
98
+ id: string;
99
+ projectKey: string;
100
+ runId: string | null;
101
+ roleId: string | null;
102
+ sessionKey: string;
103
+ target: SessionMemoryTarget;
104
+ createdAt: string;
105
+ reason: SessionMemorySegmentReason;
106
+ strength: SessionMemorySignalStrength;
107
+ source: {
108
+ traceRefId: string | null;
109
+ sourcePath: string | null;
110
+ fromOffset: number | null;
111
+ toOffset: number | null;
112
+ fromLine: number | null;
113
+ toLine: number | null;
114
+ fromEventId: string | null;
115
+ toEventId: string | null;
116
+ };
117
+ signals: SessionMemorySignal[];
118
+ rawExcerpt: {
119
+ stored: true;
120
+ encoding: "utf8";
121
+ content: string;
122
+ truncated: boolean;
123
+ byteLength: number;
124
+ sha256: string;
125
+ };
126
+ normalized: {
127
+ summary: string;
128
+ userIntent: string | null;
129
+ intentDelta: string | null;
130
+ decisions: string[];
131
+ failures: string[];
132
+ verifications: string[];
133
+ touchedTools: string[];
134
+ };
135
+ privacy: {
136
+ localOnly: true;
137
+ rawPromptStored: boolean;
138
+ rawOutputStored: boolean;
139
+ sourceContentStored: boolean;
140
+ secretsDetected: boolean;
141
+ sensitivity?: SessionMemorySensitivity;
142
+ sensitivityReasons?: SessionMemorySensitivityReason[];
143
+ redactionApplied: boolean;
144
+ externalUploadAllowed: false;
145
+ };
146
+ lifecycle: {
147
+ status: "captured" | "pending-review" | "reviewed" | "distilled" | "ignored" | "deleted";
148
+ reviewState: "not-required" | "unreviewed" | "accepted" | "rejected" | "deferred";
149
+ consumedByBatchIds: string[];
150
+ };
151
+ }
152
+
153
+ export interface SessionMemoryRawEventV1 {
154
+ schemaVersion: 1;
155
+ kind: "session-memory-raw-event";
156
+ eventId: string;
157
+ eventType: string;
158
+ occurredAt: string | null;
159
+ receivedAt: string;
160
+ summary: string;
161
+ rawPayloadJson: string;
162
+ rawPayloadByteLength: number;
163
+ rawPayloadTruncated: boolean;
164
+ secretsDetected: boolean;
165
+ sensitivity?: SessionMemorySensitivity;
166
+ sensitivityReasons?: SessionMemorySensitivityReason[];
167
+ credentialRedactionApplied?: boolean;
168
+ }
169
+
170
+ export interface SessionMemoryPaths {
171
+ rootDir: string;
172
+ projectDir: string;
173
+ sessionDir: string;
174
+ statePath: string;
175
+ cursorPath: string;
176
+ eventsPath: string;
177
+ segmentsDir: string;
178
+ segmentPath: (segmentId: string) => string;
179
+ indexPath: string;
180
+ }
181
+
182
+ export interface UpdateSessionMemoryInput {
183
+ homeDir: string;
184
+ target: SessionMemoryTarget;
185
+ event: {
186
+ eventId: string;
187
+ type: string;
188
+ time: {
189
+ occurredAt: string | null;
190
+ receivedAt: string;
191
+ };
192
+ scope: {
193
+ taskId: string | null;
194
+ };
195
+ payload: {
196
+ summary: string;
197
+ metadata: Record<string, string | number | boolean | string[]>;
198
+ };
199
+ };
200
+ rawPayload: Record<string, unknown>;
201
+ environment?: Record<string, string | undefined>;
202
+ receivedAt?: string;
203
+ policy?: Partial<SessionMemoryPolicySnapshot>;
204
+ }
205
+
206
+ export interface SessionMemoryUpdateResult {
207
+ state: SessionMemoryStateV1 | null;
208
+ cursor: SessionMemoryCursorV1 | null;
209
+ segment: SessionEvidenceSegmentV1 | null;
210
+ stateWrites: string[];
211
+ warnings: string[];
212
+ queuedSegmentTriggerId: string | null;
213
+ }
214
+
215
+ export interface SessionMemoryIndexSegment {
216
+ id: string;
217
+ reason: SessionMemorySegmentReason;
218
+ strength: SessionMemorySignalStrength;
219
+ createdAt: string;
220
+ reviewState: "not-required" | "unreviewed" | "accepted" | "rejected" | "deferred";
221
+ }