@evo-dev/core 0.0.1-alpha.11 → 0.0.1-alpha.12
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/index.js +9229 -6005
- package/package.json +1 -1
- package/src/evolution/candidates/index.ts +116 -0
- package/src/evolution/evidence/session-memory/analysis.ts +7 -1
- package/src/evolution/evidence/session-memory/index.ts +2 -0
- package/src/evolution/evidence/session-memory/retention.ts +361 -0
- package/src/evolution/evidence/session-memory/semantic-packet.ts +408 -0
- package/src/evolution/evidence/session-memory/storage.ts +336 -2
- package/src/evolution/evidence/session-memory/types.ts +75 -3
- package/src/evolution/imports/apply.ts +435 -0
- package/src/evolution/imports/diff.ts +472 -0
- package/src/evolution/imports/index.ts +7 -0
- package/src/evolution/imports/materialize.ts +640 -0
- package/src/evolution/imports/paths.ts +129 -0
- package/src/evolution/imports/stage.ts +414 -0
- package/src/evolution/imports/storage.ts +952 -0
- package/src/evolution/imports/types.ts +226 -0
- package/src/evolution/index.ts +3 -0
- package/src/evolution/schema.ts +44 -1
- package/src/evolution/shared.ts +187 -0
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
appendFile,
|
|
4
|
+
chmod,
|
|
5
|
+
link,
|
|
6
|
+
lstat,
|
|
7
|
+
mkdir,
|
|
8
|
+
open,
|
|
9
|
+
readFile,
|
|
10
|
+
readdir,
|
|
11
|
+
rename,
|
|
12
|
+
rm,
|
|
13
|
+
writeFile,
|
|
14
|
+
} from "node:fs/promises";
|
|
2
15
|
import { dirname, join } from "node:path";
|
|
3
16
|
import { resolveEvoDevPaths } from "../../../config/paths.ts";
|
|
4
|
-
import { isNotFoundError } from "../../../utils/index.ts";
|
|
17
|
+
import { isFileExistsError, isNotFoundError, sha256Hex } from "../../../utils/index.ts";
|
|
5
18
|
import { resolveSessionMemoryPaths } from "./paths.ts";
|
|
6
19
|
import { detectSessionMemorySensitivity, mergeSessionMemorySensitivity } from "./sensitivity.ts";
|
|
7
20
|
import type {
|
|
@@ -268,11 +281,332 @@ export async function writeSessionIndex(
|
|
|
268
281
|
});
|
|
269
282
|
}
|
|
270
283
|
|
|
284
|
+
export async function writeHistoricalSessionEvidenceSegment(input: {
|
|
285
|
+
homeDir: string;
|
|
286
|
+
paths: SessionMemoryPaths;
|
|
287
|
+
segment: SessionEvidenceSegmentV1;
|
|
288
|
+
}): Promise<{ created: boolean; segment: SessionEvidenceSegmentV1 }> {
|
|
289
|
+
if (input.segment.origin?.kind !== "historical-import" || input.segment.retention === undefined) {
|
|
290
|
+
throw new Error("Historical Session Evidence requires origin and retention metadata.");
|
|
291
|
+
}
|
|
292
|
+
assertSessionMemoryPathsMatchSegment(input.homeDir, input.paths, input.segment);
|
|
293
|
+
await ensurePrivateSessionMemoryDirectory(input.paths.segmentsDir);
|
|
294
|
+
const path = input.paths.segmentPath(input.segment.id);
|
|
295
|
+
const created = await writePrivateImmutableText(
|
|
296
|
+
path,
|
|
297
|
+
`${JSON.stringify(input.segment, null, 2)}\n`,
|
|
298
|
+
);
|
|
299
|
+
if (created) {
|
|
300
|
+
return { created: true, segment: input.segment };
|
|
301
|
+
}
|
|
302
|
+
const existingInfo = await lstat(path);
|
|
303
|
+
if (!existingInfo.isFile() || existingInfo.isSymbolicLink()) {
|
|
304
|
+
throw new Error("Historical Session Evidence path is not a regular state file.");
|
|
305
|
+
}
|
|
306
|
+
const existing = await readSessionEvidenceSegment({
|
|
307
|
+
homeDir: input.homeDir,
|
|
308
|
+
projectKey: input.segment.projectKey,
|
|
309
|
+
sessionKey: input.segment.sessionKey,
|
|
310
|
+
segmentId: input.segment.id,
|
|
311
|
+
});
|
|
312
|
+
assertHistoricalSegmentCompatible(existing, input.segment);
|
|
313
|
+
await chmod(path, 0o600);
|
|
314
|
+
return { created: false, segment: existing };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export async function writeHistoricalSessionStateAndIndex(input: {
|
|
318
|
+
homeDir: string;
|
|
319
|
+
paths: SessionMemoryPaths;
|
|
320
|
+
projectKey: string;
|
|
321
|
+
sessionKey: string;
|
|
322
|
+
target: SessionMemoryStateV1["target"];
|
|
323
|
+
roleId: string | null;
|
|
324
|
+
policy: SessionMemoryStateV1["policy"];
|
|
325
|
+
now: string;
|
|
326
|
+
}): Promise<{ state: SessionMemoryStateV1; segmentIds: string[] }> {
|
|
327
|
+
const expectedPaths = resolveSessionMemoryPaths({
|
|
328
|
+
homeDir: input.homeDir,
|
|
329
|
+
projectKey: input.projectKey,
|
|
330
|
+
sessionKey: input.sessionKey,
|
|
331
|
+
});
|
|
332
|
+
if (
|
|
333
|
+
expectedPaths.rootDir !== input.paths.rootDir ||
|
|
334
|
+
expectedPaths.projectDir !== input.paths.projectDir ||
|
|
335
|
+
expectedPaths.sessionDir !== input.paths.sessionDir ||
|
|
336
|
+
expectedPaths.segmentsDir !== input.paths.segmentsDir ||
|
|
337
|
+
expectedPaths.statePath !== input.paths.statePath ||
|
|
338
|
+
expectedPaths.indexPath !== input.paths.indexPath
|
|
339
|
+
) {
|
|
340
|
+
throw new Error("Historical Session Memory state path does not match its identity.");
|
|
341
|
+
}
|
|
342
|
+
await ensurePrivateSessionMemoryDirectory(input.paths.rootDir);
|
|
343
|
+
await ensurePrivateSessionMemoryDirectory(input.paths.projectDir);
|
|
344
|
+
await ensurePrivateSessionMemoryDirectory(input.paths.sessionDir);
|
|
345
|
+
await ensurePrivateSessionMemoryDirectory(input.paths.segmentsDir);
|
|
346
|
+
const existing = await readSessionState(input.paths.statePath);
|
|
347
|
+
const segments = await readSessionSegmentsFromDirectory(input.paths.segmentsDir);
|
|
348
|
+
const ordered = segments.sort(
|
|
349
|
+
(left, right) =>
|
|
350
|
+
left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id),
|
|
351
|
+
);
|
|
352
|
+
const last = ordered.at(-1) ?? null;
|
|
353
|
+
const state: SessionMemoryStateV1 = {
|
|
354
|
+
schemaVersion: 1,
|
|
355
|
+
kind: "session-memory-state",
|
|
356
|
+
projectKey: input.projectKey,
|
|
357
|
+
sessionKey: input.sessionKey,
|
|
358
|
+
target: input.target,
|
|
359
|
+
runId: null,
|
|
360
|
+
roleId: input.roleId,
|
|
361
|
+
initialized: true,
|
|
362
|
+
createdAt: existing?.createdAt ?? input.now,
|
|
363
|
+
updatedAt: input.now,
|
|
364
|
+
counters: existing?.counters ?? {
|
|
365
|
+
messageTokenEstimate: 0,
|
|
366
|
+
tokensSinceLastUpdate: 0,
|
|
367
|
+
toolCallsSinceLastUpdate: 0,
|
|
368
|
+
userPromptCount: 0,
|
|
369
|
+
assistantTurnCount: 0,
|
|
370
|
+
toolCallCount: 0,
|
|
371
|
+
failureSignalCount: 0,
|
|
372
|
+
verificationSignalCount: 0,
|
|
373
|
+
},
|
|
374
|
+
activeSpan: null,
|
|
375
|
+
pendingSignals: [],
|
|
376
|
+
lastSegmentId: last?.id ?? null,
|
|
377
|
+
lastMemoryUpdateAt: input.now,
|
|
378
|
+
policy: input.policy,
|
|
379
|
+
};
|
|
380
|
+
const index = {
|
|
381
|
+
schemaVersion: 1,
|
|
382
|
+
kind: "session-memory-index",
|
|
383
|
+
projectKey: input.projectKey,
|
|
384
|
+
sessionKey: input.sessionKey,
|
|
385
|
+
updatedAt: input.now,
|
|
386
|
+
lastSegmentId: last?.id ?? null,
|
|
387
|
+
segments: ordered.map((segment) => ({
|
|
388
|
+
id: segment.id,
|
|
389
|
+
reason: segment.reason,
|
|
390
|
+
strength: segment.strength,
|
|
391
|
+
createdAt: segment.createdAt,
|
|
392
|
+
reviewState: segment.lifecycle.reviewState,
|
|
393
|
+
})),
|
|
394
|
+
};
|
|
395
|
+
await writePrivateAtomicJson(input.paths.statePath, state);
|
|
396
|
+
await writePrivateAtomicJson(input.paths.indexPath, index);
|
|
397
|
+
return { state, segmentIds: ordered.map((segment) => segment.id) };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export async function writeSessionEvidenceSegmentAtomic(input: {
|
|
401
|
+
homeDir: string;
|
|
402
|
+
segment: SessionEvidenceSegmentV1;
|
|
403
|
+
}): Promise<void> {
|
|
404
|
+
assertHistoricalRetentionState(input.segment);
|
|
405
|
+
const paths = resolveSessionMemoryPaths({
|
|
406
|
+
homeDir: input.homeDir,
|
|
407
|
+
projectKey: input.segment.projectKey,
|
|
408
|
+
sessionKey: input.segment.sessionKey,
|
|
409
|
+
});
|
|
410
|
+
await writePrivateAtomicJson(paths.segmentPath(input.segment.id), input.segment);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function assertHistoricalRetentionState(segment: SessionEvidenceSegmentV1): void {
|
|
414
|
+
if (segment.origin?.kind !== "historical-import" || segment.retention === undefined) {
|
|
415
|
+
throw new Error("Atomic Session Evidence rewrite is limited to historical retention.");
|
|
416
|
+
}
|
|
417
|
+
if (segment.retention.rawState === "available") {
|
|
418
|
+
if (
|
|
419
|
+
!segment.rawExcerpt.stored ||
|
|
420
|
+
segment.rawExcerpt.byteLength !== Buffer.byteLength(segment.rawExcerpt.content, "utf8") ||
|
|
421
|
+
segment.rawExcerpt.sha256 !== sha256Hex(segment.rawExcerpt.content) ||
|
|
422
|
+
segment.retention.originalRawSha256 !== segment.rawExcerpt.sha256
|
|
423
|
+
) {
|
|
424
|
+
throw new Error("Available historical Session Evidence raw state is invalid.");
|
|
425
|
+
}
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (
|
|
429
|
+
segment.rawExcerpt.stored ||
|
|
430
|
+
segment.rawExcerpt.content !== "" ||
|
|
431
|
+
segment.rawExcerpt.byteLength !== 0 ||
|
|
432
|
+
segment.rawExcerpt.sha256 !== sha256Hex("") ||
|
|
433
|
+
segment.retention.rawPurgedAt === null ||
|
|
434
|
+
segment.lifecycle.status !== "raw-expired"
|
|
435
|
+
) {
|
|
436
|
+
throw new Error("Purged historical Session Evidence raw state is invalid.");
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
271
440
|
export async function writeJson(path: string, value: unknown): Promise<void> {
|
|
272
441
|
await mkdir(dirname(path), { recursive: true });
|
|
273
442
|
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
274
443
|
}
|
|
275
444
|
|
|
445
|
+
async function readSessionSegmentsFromDirectory(
|
|
446
|
+
segmentsDir: string,
|
|
447
|
+
): Promise<SessionEvidenceSegmentV1[]> {
|
|
448
|
+
const segments: SessionEvidenceSegmentV1[] = [];
|
|
449
|
+
for (const path of await listJsonFiles(segmentsDir)) {
|
|
450
|
+
segments.push(parseSessionEvidenceSegment(JSON.parse(await readFile(path, "utf8"))));
|
|
451
|
+
}
|
|
452
|
+
return segments;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async function ensurePrivateSessionMemoryDirectory(path: string): Promise<void> {
|
|
456
|
+
await mkdir(path, { recursive: true, mode: 0o700 });
|
|
457
|
+
await chmod(path, 0o700);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async function writePrivateAtomicJson(path: string, value: unknown): Promise<void> {
|
|
461
|
+
await ensurePrivateSessionMemoryDirectory(dirname(path));
|
|
462
|
+
const temporaryPath = join(dirname(path), `.${randomUUID()}.tmp`);
|
|
463
|
+
try {
|
|
464
|
+
const handle = await open(temporaryPath, "wx", 0o600);
|
|
465
|
+
try {
|
|
466
|
+
await handle.writeFile(`${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
467
|
+
await handle.sync();
|
|
468
|
+
} finally {
|
|
469
|
+
await handle.close();
|
|
470
|
+
}
|
|
471
|
+
await rename(temporaryPath, path);
|
|
472
|
+
await chmod(path, 0o600);
|
|
473
|
+
} finally {
|
|
474
|
+
await rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function writePrivateImmutableText(path: string, content: string): Promise<boolean> {
|
|
479
|
+
await ensurePrivateSessionMemoryDirectory(dirname(path));
|
|
480
|
+
const temporaryPath = join(dirname(path), `.${randomUUID()}.tmp`);
|
|
481
|
+
try {
|
|
482
|
+
const handle = await open(temporaryPath, "wx", 0o600);
|
|
483
|
+
try {
|
|
484
|
+
await handle.writeFile(content, "utf8");
|
|
485
|
+
await handle.sync();
|
|
486
|
+
} finally {
|
|
487
|
+
await handle.close();
|
|
488
|
+
}
|
|
489
|
+
try {
|
|
490
|
+
await link(temporaryPath, path);
|
|
491
|
+
await chmod(path, 0o600);
|
|
492
|
+
return true;
|
|
493
|
+
} catch (error) {
|
|
494
|
+
if (!isFileExistsError(error)) throw error;
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
} finally {
|
|
498
|
+
await rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function assertHistoricalSegmentCompatible(
|
|
503
|
+
existing: SessionEvidenceSegmentV1,
|
|
504
|
+
expected: SessionEvidenceSegmentV1,
|
|
505
|
+
): void {
|
|
506
|
+
const existingOrigin = existing.origin;
|
|
507
|
+
const expectedOrigin = expected.origin;
|
|
508
|
+
const existingRetention = existing.retention;
|
|
509
|
+
const expectedRetention = expected.retention;
|
|
510
|
+
if (
|
|
511
|
+
existingOrigin?.kind !== "historical-import" ||
|
|
512
|
+
expectedOrigin?.kind !== "historical-import" ||
|
|
513
|
+
existingRetention === undefined ||
|
|
514
|
+
expectedRetention === undefined
|
|
515
|
+
) {
|
|
516
|
+
throw new Error("Historical Session Evidence identity conflict.");
|
|
517
|
+
}
|
|
518
|
+
const existingProjection = {
|
|
519
|
+
schemaVersion: existing.schemaVersion,
|
|
520
|
+
kind: existing.kind,
|
|
521
|
+
id: existing.id,
|
|
522
|
+
projectKey: existing.projectKey,
|
|
523
|
+
runId: existing.runId,
|
|
524
|
+
roleId: existing.roleId,
|
|
525
|
+
sessionKey: existing.sessionKey,
|
|
526
|
+
target: existing.target,
|
|
527
|
+
createdAt: existing.createdAt,
|
|
528
|
+
reason: existing.reason,
|
|
529
|
+
strength: existing.strength,
|
|
530
|
+
origin: existingOrigin,
|
|
531
|
+
source: existing.source,
|
|
532
|
+
signals: existing.signals,
|
|
533
|
+
normalized: existing.normalized,
|
|
534
|
+
privacy: {
|
|
535
|
+
localOnly: existing.privacy.localOnly,
|
|
536
|
+
secretsDetected: existing.privacy.secretsDetected,
|
|
537
|
+
sensitivity: existing.privacy.sensitivity,
|
|
538
|
+
sensitivityReasons: existing.privacy.sensitivityReasons,
|
|
539
|
+
redactionApplied: existing.privacy.redactionApplied,
|
|
540
|
+
externalUploadAllowed: existing.privacy.externalUploadAllowed,
|
|
541
|
+
},
|
|
542
|
+
rawTruncated: existing.rawExcerpt.truncated,
|
|
543
|
+
originalRawSha256: existingRetention.originalRawSha256,
|
|
544
|
+
retentionPolicyDays: existingRetention.policyDays,
|
|
545
|
+
retentionExpiresAt: existingRetention.expiresAt,
|
|
546
|
+
};
|
|
547
|
+
const expectedProjection = {
|
|
548
|
+
schemaVersion: expected.schemaVersion,
|
|
549
|
+
kind: expected.kind,
|
|
550
|
+
id: expected.id,
|
|
551
|
+
projectKey: expected.projectKey,
|
|
552
|
+
runId: expected.runId,
|
|
553
|
+
roleId: expected.roleId,
|
|
554
|
+
sessionKey: expected.sessionKey,
|
|
555
|
+
target: expected.target,
|
|
556
|
+
createdAt: expected.createdAt,
|
|
557
|
+
reason: expected.reason,
|
|
558
|
+
strength: expected.strength,
|
|
559
|
+
origin: expectedOrigin,
|
|
560
|
+
source: expected.source,
|
|
561
|
+
signals: expected.signals,
|
|
562
|
+
normalized: expected.normalized,
|
|
563
|
+
privacy: {
|
|
564
|
+
localOnly: expected.privacy.localOnly,
|
|
565
|
+
secretsDetected: expected.privacy.secretsDetected,
|
|
566
|
+
sensitivity: expected.privacy.sensitivity,
|
|
567
|
+
sensitivityReasons: expected.privacy.sensitivityReasons,
|
|
568
|
+
redactionApplied: expected.privacy.redactionApplied,
|
|
569
|
+
externalUploadAllowed: expected.privacy.externalUploadAllowed,
|
|
570
|
+
},
|
|
571
|
+
rawTruncated: expected.rawExcerpt.truncated,
|
|
572
|
+
originalRawSha256: expectedRetention.originalRawSha256,
|
|
573
|
+
retentionPolicyDays: expectedRetention.policyDays,
|
|
574
|
+
retentionExpiresAt: expectedRetention.expiresAt,
|
|
575
|
+
};
|
|
576
|
+
if (
|
|
577
|
+
JSON.stringify(existingProjection) !== JSON.stringify(expectedProjection) ||
|
|
578
|
+
(existingRetention.rawState === "available" &&
|
|
579
|
+
(!existing.rawExcerpt.stored ||
|
|
580
|
+
existing.rawExcerpt.encoding !== "utf8" ||
|
|
581
|
+
existing.rawExcerpt.byteLength !== Buffer.byteLength(existing.rawExcerpt.content, "utf8") ||
|
|
582
|
+
existing.rawExcerpt.sha256 !== sha256Hex(existing.rawExcerpt.content) ||
|
|
583
|
+
existing.rawExcerpt.sha256 !== expected.rawExcerpt.sha256))
|
|
584
|
+
) {
|
|
585
|
+
throw new Error("Historical Session Evidence immutable content conflict.");
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function assertSessionMemoryPathsMatchSegment(
|
|
590
|
+
homeDir: string,
|
|
591
|
+
paths: SessionMemoryPaths,
|
|
592
|
+
segment: SessionEvidenceSegmentV1,
|
|
593
|
+
): void {
|
|
594
|
+
const expected = resolveSessionMemoryPaths({
|
|
595
|
+
homeDir,
|
|
596
|
+
projectKey: segment.projectKey,
|
|
597
|
+
sessionKey: segment.sessionKey,
|
|
598
|
+
});
|
|
599
|
+
if (
|
|
600
|
+
paths.rootDir !== expected.rootDir ||
|
|
601
|
+
paths.projectDir !== expected.projectDir ||
|
|
602
|
+
paths.sessionDir !== expected.sessionDir ||
|
|
603
|
+
paths.segmentsDir !== expected.segmentsDir ||
|
|
604
|
+
paths.segmentPath(segment.id) !== expected.segmentPath(segment.id)
|
|
605
|
+
) {
|
|
606
|
+
throw new Error("Historical Session Evidence path does not match its identity.");
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
276
610
|
async function countJsonlLines(path: string): Promise<number> {
|
|
277
611
|
try {
|
|
278
612
|
const text = await readFile(path, "utf8");
|
|
@@ -7,7 +7,8 @@ export type SessionMemorySegmentReason =
|
|
|
7
7
|
| "failure-signal"
|
|
8
8
|
| "permission-denied"
|
|
9
9
|
| "verification-after-fix"
|
|
10
|
-
| "explicit-memory-intent"
|
|
10
|
+
| "explicit-memory-intent"
|
|
11
|
+
| "historical-import";
|
|
11
12
|
export type SessionMemorySignalStrength = "normal" | "strong";
|
|
12
13
|
export type SessionMemorySensitivity = "safe-metadata" | "private-content" | "credential";
|
|
13
14
|
export type SessionMemorySensitivityReason =
|
|
@@ -92,6 +93,68 @@ export interface SessionMemoryCursorV1 {
|
|
|
92
93
|
updatedAt: string;
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
export interface HistoricalImportOriginV1 {
|
|
97
|
+
kind: "historical-import";
|
|
98
|
+
importId: string;
|
|
99
|
+
generationId: string;
|
|
100
|
+
snapshotId: string;
|
|
101
|
+
sourceKey: string;
|
|
102
|
+
firstRecordKeyHash: string;
|
|
103
|
+
lastRecordKeyHash: string;
|
|
104
|
+
recordCount: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface HistoricalImportRetentionV1 {
|
|
108
|
+
policyDays: number;
|
|
109
|
+
expiresAt: string;
|
|
110
|
+
rawState: "available" | "purged";
|
|
111
|
+
rawPurgedAt: string | null;
|
|
112
|
+
originalRawSha256: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface HistoricalImportStoredRecordV1 {
|
|
116
|
+
schemaVersion: 1;
|
|
117
|
+
kind: "historical-import-record";
|
|
118
|
+
recordKeyHash: string;
|
|
119
|
+
recordType: "user" | "reasoning" | "assistant" | "assistant-tool-call" | "tool";
|
|
120
|
+
canonicalJson: string;
|
|
121
|
+
canonicalJsonTruncated: boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface SemanticEvidencePacketV1 {
|
|
125
|
+
schemaVersion: 1;
|
|
126
|
+
kind: "semantic-evidence-packet";
|
|
127
|
+
projectKey: string;
|
|
128
|
+
segmentId: string;
|
|
129
|
+
conversation: Array<{
|
|
130
|
+
role: "user" | "assistant";
|
|
131
|
+
text: string;
|
|
132
|
+
recordKeyHash: string;
|
|
133
|
+
}>;
|
|
134
|
+
toolEvidence: Array<{
|
|
135
|
+
reason: "failure" | "verification" | "permission" | "rollback";
|
|
136
|
+
toolName: string;
|
|
137
|
+
safeInputExcerpt: string | null;
|
|
138
|
+
outputExcerpt: string | null;
|
|
139
|
+
status: string | null;
|
|
140
|
+
exitCode: number | null;
|
|
141
|
+
inputTruncated: boolean;
|
|
142
|
+
outputTruncated: boolean;
|
|
143
|
+
}>;
|
|
144
|
+
touchedPaths: string[];
|
|
145
|
+
selection: {
|
|
146
|
+
selectedRecords: number;
|
|
147
|
+
droppedRecords: number;
|
|
148
|
+
redactionCount: number;
|
|
149
|
+
truncated: boolean;
|
|
150
|
+
};
|
|
151
|
+
privacy: {
|
|
152
|
+
containsPrivateContent: true;
|
|
153
|
+
credentialRedacted: true;
|
|
154
|
+
rawSegmentIncluded: false;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
95
158
|
export interface SessionEvidenceSegmentV1 {
|
|
96
159
|
schemaVersion: 1;
|
|
97
160
|
kind: "session-evidence-segment";
|
|
@@ -104,6 +167,8 @@ export interface SessionEvidenceSegmentV1 {
|
|
|
104
167
|
createdAt: string;
|
|
105
168
|
reason: SessionMemorySegmentReason;
|
|
106
169
|
strength: SessionMemorySignalStrength;
|
|
170
|
+
origin?: HistoricalImportOriginV1;
|
|
171
|
+
retention?: HistoricalImportRetentionV1;
|
|
107
172
|
source: {
|
|
108
173
|
traceRefId: string | null;
|
|
109
174
|
sourcePath: string | null;
|
|
@@ -116,7 +181,7 @@ export interface SessionEvidenceSegmentV1 {
|
|
|
116
181
|
};
|
|
117
182
|
signals: SessionMemorySignal[];
|
|
118
183
|
rawExcerpt: {
|
|
119
|
-
stored:
|
|
184
|
+
stored: boolean;
|
|
120
185
|
encoding: "utf8";
|
|
121
186
|
content: string;
|
|
122
187
|
truncated: boolean;
|
|
@@ -144,7 +209,14 @@ export interface SessionEvidenceSegmentV1 {
|
|
|
144
209
|
externalUploadAllowed: false;
|
|
145
210
|
};
|
|
146
211
|
lifecycle: {
|
|
147
|
-
status:
|
|
212
|
+
status:
|
|
213
|
+
| "captured"
|
|
214
|
+
| "pending-review"
|
|
215
|
+
| "reviewed"
|
|
216
|
+
| "distilled"
|
|
217
|
+
| "ignored"
|
|
218
|
+
| "deleted"
|
|
219
|
+
| "raw-expired";
|
|
148
220
|
reviewState: "not-required" | "unreviewed" | "accepted" | "rejected" | "deferred";
|
|
149
221
|
consumedByBatchIds: string[];
|
|
150
222
|
};
|