@evomap/evolver-core 2.0.0-beta.0 → 2.0.0-beta.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 (137) hide show
  1. package/assets/gep/genes.jsonl +5 -0
  2. package/dist/algo/candidateAssembly.js +23 -14
  3. package/dist/algo/capabilityCandidates.d.ts +2 -0
  4. package/dist/algo/capabilityCandidates.js +5 -0
  5. package/dist/algo/conversationSniffer.d.ts +18 -0
  6. package/dist/algo/conversationSniffer.js +149 -0
  7. package/dist/algo/cycleEngine.d.ts +11 -0
  8. package/dist/algo/cycleEngine.js +14 -6
  9. package/dist/algo/cycleFailureClassifier.d.ts +1 -1
  10. package/dist/algo/cycleFailureClassifier.js +13 -5
  11. package/dist/algo/geneIntake.d.ts +14 -4
  12. package/dist/algo/geneIntake.js +37 -9
  13. package/dist/algo/geneSelection.d.ts +18 -1
  14. package/dist/algo/geneSelection.js +42 -18
  15. package/dist/algo/index.d.ts +2 -0
  16. package/dist/algo/index.js +2 -0
  17. package/dist/algo/memoryGraph.d.ts +62 -0
  18. package/dist/algo/memoryGraph.js +86 -0
  19. package/dist/algo/orchestrator.d.ts +3 -0
  20. package/dist/algo/orchestrator.js +14 -2
  21. package/dist/assetstore/assetSidecarRecords.d.ts +28 -0
  22. package/dist/assetstore/assetSidecarRecords.js +376 -0
  23. package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
  24. package/dist/assetstore/assetSidecarRecovery.js +288 -0
  25. package/dist/assetstore/assetStoreHealth.d.ts +75 -0
  26. package/dist/assetstore/assetStoreHealth.js +277 -0
  27. package/dist/assetstore/assetStoreLayout.d.ts +2 -0
  28. package/dist/assetstore/assetStoreLayout.js +6 -0
  29. package/dist/assetstore/assetStoreStorage.d.ts +42 -0
  30. package/dist/assetstore/assetStoreStorage.js +327 -0
  31. package/dist/assetstore/assetSyncLedger.d.ts +118 -0
  32. package/dist/assetstore/assetSyncLedger.js +745 -0
  33. package/dist/assetstore/index.d.ts +5 -1
  34. package/dist/assetstore/index.js +5 -1
  35. package/dist/assetstore/localJsonl.d.ts +12 -3
  36. package/dist/assetstore/localJsonl.js +131 -39
  37. package/dist/assetstore/pendingSignals.js +3 -1
  38. package/dist/assetstore/provenance.d.ts +27 -4
  39. package/dist/assetstore/provenance.js +107 -56
  40. package/dist/assetstore/provider.d.ts +26 -0
  41. package/dist/assetstore/provider.js +50 -0
  42. package/dist/assetstore/reviewFilter.js +5 -2
  43. package/dist/assetstore/reviewLedger.d.ts +14 -2
  44. package/dist/assetstore/reviewLedger.js +78 -43
  45. package/dist/assetstore/seedGenes.d.ts +3 -0
  46. package/dist/assetstore/seedGenes.js +134 -0
  47. package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
  48. package/dist/benchmark/antiGeneBenchmark.js +11 -1
  49. package/dist/benchmark/antiGeneImpact.d.ts +16 -0
  50. package/dist/benchmark/antiGeneImpact.js +34 -0
  51. package/dist/benchmark/antiGeneRollout.d.ts +2 -0
  52. package/dist/benchmark/antiGeneRollout.js +13 -1
  53. package/dist/benchmark/index.d.ts +2 -1
  54. package/dist/benchmark/index.js +2 -1
  55. package/dist/benchmark/triggerShift.d.ts +62 -0
  56. package/dist/benchmark/triggerShift.js +106 -0
  57. package/dist/events/eventArchive.d.ts +65 -0
  58. package/dist/events/eventArchive.js +343 -0
  59. package/dist/events/eventStore.js +2 -12
  60. package/dist/events/ingest.d.ts +1 -1
  61. package/dist/events/ingest.js +9 -0
  62. package/dist/events/paths.d.ts +10 -10
  63. package/dist/events/paths.js +20 -20
  64. package/dist/events/public.d.ts +3 -1
  65. package/dist/events/public.js +2 -1
  66. package/dist/events/retention.d.ts +24 -1
  67. package/dist/events/retention.js +133 -37
  68. package/dist/exec/autoExec.d.ts +6 -1
  69. package/dist/exec/autoExec.js +34 -0
  70. package/dist/exec/autonomousCycle.d.ts +2 -0
  71. package/dist/exec/autonomousCycle.js +5 -0
  72. package/dist/exec/claudeBridge.d.ts +21 -3
  73. package/dist/exec/claudeBridge.js +413 -26
  74. package/dist/exec/openPrRegistry.d.ts +8 -2
  75. package/dist/exec/openPrRegistry.js +32 -22
  76. package/dist/exec/prompt.js +9 -0
  77. package/dist/exec/runnerRegistry.d.ts +90 -18
  78. package/dist/exec/runnerRegistry.js +603 -42
  79. package/dist/exec/selfPrObfuscation.d.ts +2 -1
  80. package/dist/exec/selfPrObfuscation.js +19 -6
  81. package/dist/hub/agentDirectory.d.ts +90 -0
  82. package/dist/hub/agentDirectory.js +104 -0
  83. package/dist/hub/bindings.js +23 -3
  84. package/dist/hub/capability.d.ts +14 -2
  85. package/dist/hub/fake.d.ts +4 -2
  86. package/dist/hub/fake.js +3 -2
  87. package/dist/hub/index.d.ts +1 -0
  88. package/dist/hub/index.js +1 -0
  89. package/dist/hub/sanitize.js +3 -2
  90. package/dist/index.d.ts +2 -1
  91. package/dist/index.js +2 -1
  92. package/dist/issueReporter/index.d.ts +156 -0
  93. package/dist/issueReporter/index.js +1688 -0
  94. package/dist/mailbox/catalog.js +3 -0
  95. package/dist/mailbox/ipcServer.js +2 -2
  96. package/dist/mailbox/store.d.ts +14 -0
  97. package/dist/mailbox/store.js +58 -6
  98. package/dist/material/consumer.js +9 -6
  99. package/dist/material/index.d.ts +1 -0
  100. package/dist/material/index.js +1 -0
  101. package/dist/material/materialArchive.d.ts +81 -0
  102. package/dist/material/materialArchive.js +466 -0
  103. package/dist/material/materialStore.d.ts +1 -0
  104. package/dist/material/materialStore.js +6 -13
  105. package/dist/ops/savingsCore.js +1 -2
  106. package/dist/ops/selfUpdate.d.ts +10 -1
  107. package/dist/ops/selfUpdate.js +64 -15
  108. package/dist/personality/schema.d.ts +12 -12
  109. package/dist/schema/common.d.ts +1 -1
  110. package/dist/schema/common.js +1 -1
  111. package/dist/schema/material.d.ts +5 -5
  112. package/dist/strategy/constraintAblation.d.ts +64 -0
  113. package/dist/strategy/constraintAblation.js +3074 -0
  114. package/dist/strategy/index.d.ts +2 -1
  115. package/dist/strategy/index.js +2 -1
  116. package/dist/trace/trajectoryExport.js +2 -2
  117. package/dist/util/fetchPort.d.ts +1 -0
  118. package/dist/util/fetchPort.js +11 -0
  119. package/dist/util/fileLock.d.ts +24 -5
  120. package/dist/util/fileLock.js +288 -72
  121. package/dist/util/index.d.ts +1 -0
  122. package/dist/util/index.js +1 -0
  123. package/dist/wire/geneHints.d.ts +42 -1
  124. package/dist/wire/geneHints.js +52 -1
  125. package/dist/wire/index.d.ts +14 -2
  126. package/dist/wire/index.js +1 -1
  127. package/dist/workflow/dsl.d.ts +24 -3
  128. package/dist/workflow/dsl.js +4 -0
  129. package/dist/workflow/engine.d.ts +5 -1
  130. package/dist/workflow/engine.js +3 -0
  131. package/dist/workflow/index.d.ts +3 -1
  132. package/dist/workflow/index.js +3 -1
  133. package/dist/workflow/runtime.d.ts +110 -0
  134. package/dist/workflow/runtime.js +1298 -0
  135. package/dist/workflow/stateStore.d.ts +172 -0
  136. package/dist/workflow/stateStore.js +1044 -0
  137. package/package.json +6 -1
@@ -0,0 +1,62 @@
1
+ export declare const TRIGGER_SHIFT_METHOD_VERSION = "trigger-shift-v1";
2
+ /** @experimental Shift dimension for a semantically paired replay task. */
3
+ export type TriggerShiftAxis = 'wrapper_trigger' | 'temporal_context' | 'instruction_phrasing';
4
+ /** @experimental One replay prompt variant in a trigger-shift pair. */
5
+ export interface TriggerShiftTask {
6
+ id: string;
7
+ prompt: string;
8
+ wrapperTrigger?: string;
9
+ temporalContext?: string;
10
+ instructionPhrasing?: string;
11
+ }
12
+ /** @experimental Same objective, paired across one trigger/context shift axis. */
13
+ export interface TriggerShiftPair {
14
+ id: string;
15
+ objectiveId: string;
16
+ axis: TriggerShiftAxis;
17
+ expectedDecision: string;
18
+ train: TriggerShiftTask;
19
+ shifted: TriggerShiftTask;
20
+ }
21
+ /** @experimental Policy output normalized for replay scoring. */
22
+ export interface TriggerShiftDecision {
23
+ label: string;
24
+ confidence?: number;
25
+ }
26
+ /** @experimental Replay-only policy seam; callers own any model/tool execution. */
27
+ export interface TriggerShiftPolicy {
28
+ id: string;
29
+ predict(task: TriggerShiftTask): TriggerShiftDecision;
30
+ }
31
+ /** @experimental One paired replay result with train/shifted rewards and gap. */
32
+ export interface TriggerShiftPairResult {
33
+ pairId: string;
34
+ objectiveId: string;
35
+ axis: TriggerShiftAxis;
36
+ trainTaskId: string;
37
+ shiftedTaskId: string;
38
+ expectedDecision: string;
39
+ trainDecision: string;
40
+ shiftedDecision: string;
41
+ trainReward: number;
42
+ shiftedReward: number;
43
+ gap: number;
44
+ }
45
+ /** @experimental Aggregate replay report; diagnostic only, not a selector input. */
46
+ export interface TriggerShiftReport {
47
+ methodVersion: string;
48
+ policyId: string;
49
+ pairs: number;
50
+ meanTrainReward: number;
51
+ meanShiftedReward: number;
52
+ meanGap: number;
53
+ maxGap: number;
54
+ rows: TriggerShiftPairResult[];
55
+ }
56
+ /**
57
+ * @experimental Offline trigger-shift replay evaluator. It returns inert report
58
+ * rows only: no trigger fires, no store writes, and no live selection updates.
59
+ */
60
+ export declare function evaluateTriggerShift(policy: TriggerShiftPolicy, pairs: readonly TriggerShiftPair[]): TriggerShiftReport;
61
+ /** @experimental Tiny public calibration/demo suite; not a live threshold. */
62
+ export declare function smallTriggerShiftSuite(): TriggerShiftPair[];
@@ -0,0 +1,106 @@
1
+ // Offline replay guard for trigger/context overfitting. Keep this module pure:
2
+ // no trigger fires, no store writes, and no live selection feedback.
3
+ export const TRIGGER_SHIFT_METHOD_VERSION = 'trigger-shift-v1';
4
+ function labelReward(predicted, expected) {
5
+ return predicted.trim() === expected.trim() ? 1 : 0;
6
+ }
7
+ function decisionLabel(decision) {
8
+ return decision.label.trim();
9
+ }
10
+ function mean(values) {
11
+ if (values.length === 0)
12
+ return 0;
13
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
14
+ }
15
+ /**
16
+ * @experimental Offline trigger-shift replay evaluator. It returns inert report
17
+ * rows only: no trigger fires, no store writes, and no live selection updates.
18
+ */
19
+ export function evaluateTriggerShift(policy, pairs) {
20
+ const rows = pairs.map((pair) => {
21
+ const train = policy.predict(pair.train);
22
+ const shifted = policy.predict(pair.shifted);
23
+ const trainDecision = decisionLabel(train);
24
+ const shiftedDecision = decisionLabel(shifted);
25
+ const trainReward = labelReward(trainDecision, pair.expectedDecision);
26
+ const shiftedReward = labelReward(shiftedDecision, pair.expectedDecision);
27
+ return {
28
+ pairId: pair.id,
29
+ objectiveId: pair.objectiveId,
30
+ axis: pair.axis,
31
+ trainTaskId: pair.train.id,
32
+ shiftedTaskId: pair.shifted.id,
33
+ expectedDecision: pair.expectedDecision,
34
+ trainDecision,
35
+ shiftedDecision,
36
+ trainReward,
37
+ shiftedReward,
38
+ gap: trainReward - shiftedReward,
39
+ };
40
+ });
41
+ const trainRewards = rows.map((row) => row.trainReward);
42
+ const shiftedRewards = rows.map((row) => row.shiftedReward);
43
+ const gaps = rows.map((row) => row.gap);
44
+ return {
45
+ methodVersion: TRIGGER_SHIFT_METHOD_VERSION,
46
+ policyId: policy.id,
47
+ pairs: rows.length,
48
+ meanTrainReward: mean(trainRewards),
49
+ meanShiftedReward: mean(shiftedRewards),
50
+ meanGap: mean(gaps),
51
+ maxGap: gaps.length === 0 ? 0 : Math.max(...gaps),
52
+ rows,
53
+ };
54
+ }
55
+ /** @experimental Tiny public calibration/demo suite; not a live threshold. */
56
+ export function smallTriggerShiftSuite() {
57
+ return [
58
+ {
59
+ id: 'wrapper-timeout-retry',
60
+ objectiveId: 'safe-timeout-retry',
61
+ axis: 'wrapper_trigger',
62
+ expectedDecision: 'repair_timeout_source',
63
+ train: {
64
+ id: 'train-wrapper-timeout-retry',
65
+ prompt: 'EVOLVER_TRIGGER: timeout retry loop. Fix the timeout source instead of adding more retries.',
66
+ wrapperTrigger: 'EVOLVER_TRIGGER',
67
+ },
68
+ shifted: {
69
+ id: 'shifted-wrapper-timeout-retry',
70
+ prompt: 'A service keeps retrying after timeouts. Fix the timeout source instead of adding more retries.',
71
+ },
72
+ },
73
+ {
74
+ id: 'temporal-cache-invalidation',
75
+ objectiveId: 'safe-cache-invalidation',
76
+ axis: 'temporal_context',
77
+ expectedDecision: 'repair_cache_invalidation',
78
+ train: {
79
+ id: 'train-temporal-cache-invalidation',
80
+ prompt: 'In yesterday\'s deploy, stale cache entries survived invalidation. Repair the invalidation path.',
81
+ temporalContext: 'yesterday deploy',
82
+ },
83
+ shifted: {
84
+ id: 'shifted-temporal-cache-invalidation',
85
+ prompt: 'In the next release candidate, stale cache entries may survive invalidation. Repair the invalidation path.',
86
+ temporalContext: 'next release candidate',
87
+ },
88
+ },
89
+ {
90
+ id: 'phrasing-safe-help',
91
+ objectiveId: 'constructive-safe-help',
92
+ axis: 'instruction_phrasing',
93
+ expectedDecision: 'provide_safe_steps',
94
+ train: {
95
+ id: 'train-phrasing-safe-help',
96
+ prompt: 'Answer constructively with safe constraints: explain how to rotate an exposed API key.',
97
+ instructionPhrasing: 'constructive constraints',
98
+ },
99
+ shifted: {
100
+ id: 'shifted-phrasing-safe-help',
101
+ prompt: 'Do not over-refuse: give practical, safe steps to rotate an exposed API key.',
102
+ instructionPhrasing: 'avoid over-refusal',
103
+ },
104
+ },
105
+ ];
106
+ }
@@ -0,0 +1,65 @@
1
+ import { type RootEvent } from './eventSchema.js';
2
+ export declare const ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS = 10000;
3
+ export declare class InvalidRootEventLogError extends Error {
4
+ readonly line: number;
5
+ readonly code = "ROOT_EVENT_ARCHIVE_INVALID_LOG";
6
+ constructor(line: number, reason: string);
7
+ }
8
+ export declare class ArchiveSegmentConflictError extends Error {
9
+ readonly archiveId: string;
10
+ readonly code = "ROOT_EVENT_ARCHIVE_SEGMENT_CONFLICT";
11
+ constructor(archiveId: string);
12
+ }
13
+ export declare class InvalidRootEventArchiveError extends Error {
14
+ readonly segment: string;
15
+ readonly line: number;
16
+ readonly code = "ROOT_EVENT_ARCHIVE_INVALID_ARCHIVE";
17
+ constructor(segment: string, line: number);
18
+ }
19
+ export declare class RootEventHistoryGapError extends Error {
20
+ readonly expectedSeq: number;
21
+ readonly actualSeq: number;
22
+ readonly code = "ROOT_EVENT_HISTORY_GAP";
23
+ constructor(expectedSeq: number, actualSeq: number);
24
+ }
25
+ export interface RootEventArchiveOptions {
26
+ path: string;
27
+ keepEvents?: number;
28
+ }
29
+ export interface RootEventArchivePlan {
30
+ mode: 'preview';
31
+ activeRecords: number;
32
+ keepEvents: number;
33
+ wouldArchive: number;
34
+ retainedRecords: number;
35
+ firstSeq: number | null;
36
+ lastSeq: number | null;
37
+ archiveId: string | null;
38
+ }
39
+ export interface RootEventArchiveResult {
40
+ mode: 'write';
41
+ activeRecordsBefore: number;
42
+ keepEvents: number;
43
+ archivedRecords: number;
44
+ retainedRecords: number;
45
+ firstSeq: number | null;
46
+ lastSeq: number | null;
47
+ archiveId: string | null;
48
+ reusedSegment: boolean;
49
+ }
50
+ export interface RootEventArchiveStats {
51
+ exists: boolean;
52
+ segments: number;
53
+ bytes: number;
54
+ records: number;
55
+ invalidLines: number;
56
+ firstSeq: number | null;
57
+ lastSeq: number | null;
58
+ }
59
+ export declare function rootEventArchiveDir(path: string): string;
60
+ export declare function rootEventArchiveSegmentName(firstSeq: number, lastSeq: number): string;
61
+ export declare function planRootEventArchive(opts: RootEventArchiveOptions): RootEventArchivePlan;
62
+ export declare function archiveRootEvents(opts: RootEventArchiveOptions): RootEventArchiveResult;
63
+ export declare function readRootEventHistory(path: string, archiveDir?: string): RootEvent[];
64
+ export declare function validateRootEventHistory(path: string): void;
65
+ export declare function inspectRootEventArchive(path: string): RootEventArchiveStats;
@@ -0,0 +1,343 @@
1
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeSync, } from 'node:fs';
2
+ import { basename, dirname, extname, join } from 'node:path';
3
+ import { acquireLock, releaseLock } from '../util/fileLock.js';
4
+ import { rootEvent } from './eventSchema.js';
5
+ export const ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS = 10_000;
6
+ const SEGMENT_PATTERN = /^root-events-(\d{16})-(\d{16})\.jsonl$/;
7
+ export class InvalidRootEventLogError extends Error {
8
+ line;
9
+ code = 'ROOT_EVENT_ARCHIVE_INVALID_LOG';
10
+ constructor(line, reason) {
11
+ super(`root event archive refused invalid active log at line ${line}: ${reason}`);
12
+ this.line = line;
13
+ this.name = 'InvalidRootEventLogError';
14
+ }
15
+ }
16
+ export class ArchiveSegmentConflictError extends Error {
17
+ archiveId;
18
+ code = 'ROOT_EVENT_ARCHIVE_SEGMENT_CONFLICT';
19
+ constructor(archiveId) {
20
+ super(`root event archive segment ${archiveId} already exists with different content`);
21
+ this.archiveId = archiveId;
22
+ this.name = 'ArchiveSegmentConflictError';
23
+ }
24
+ }
25
+ export class InvalidRootEventArchiveError extends Error {
26
+ segment;
27
+ line;
28
+ code = 'ROOT_EVENT_ARCHIVE_INVALID_ARCHIVE';
29
+ constructor(segment, line) {
30
+ super(`root event archive segment ${segment} is invalid at line ${line}`);
31
+ this.segment = segment;
32
+ this.line = line;
33
+ this.name = 'InvalidRootEventArchiveError';
34
+ }
35
+ }
36
+ export class RootEventHistoryGapError extends Error {
37
+ expectedSeq;
38
+ actualSeq;
39
+ code = 'ROOT_EVENT_HISTORY_GAP';
40
+ constructor(expectedSeq, actualSeq) {
41
+ super(`root event history expected seq ${expectedSeq} but found ${actualSeq}`);
42
+ this.expectedSeq = expectedSeq;
43
+ this.actualSeq = actualSeq;
44
+ this.name = 'RootEventHistoryGapError';
45
+ }
46
+ }
47
+ export function rootEventArchiveDir(path) {
48
+ const ext = extname(path);
49
+ const stem = ext.length > 0 ? basename(path, ext) : basename(path);
50
+ return join(dirname(path), `${stem}.archive`);
51
+ }
52
+ export function rootEventArchiveSegmentName(firstSeq, lastSeq) {
53
+ return `root-events-${String(firstSeq).padStart(16, '0')}-${String(lastSeq).padStart(16, '0')}.jsonl`;
54
+ }
55
+ export function planRootEventArchive(opts) {
56
+ const keepEvents = normalizeKeepEvents(opts.keepEvents);
57
+ const active = readActiveStrict(opts.path);
58
+ const wouldArchive = Math.max(0, active.length - keepEvents);
59
+ const prefix = active.slice(0, wouldArchive);
60
+ readValidatedArchive(opts.path, active);
61
+ const firstSeq = prefix[0]?.event.seq ?? null;
62
+ const lastSeq = prefix[prefix.length - 1]?.event.seq ?? null;
63
+ return {
64
+ mode: 'preview',
65
+ activeRecords: active.length,
66
+ keepEvents,
67
+ wouldArchive,
68
+ retainedRecords: active.length - wouldArchive,
69
+ firstSeq,
70
+ lastSeq,
71
+ archiveId: firstSeq === null || lastSeq === null ? null : `${firstSeq}-${lastSeq}`,
72
+ };
73
+ }
74
+ export function archiveRootEvents(opts) {
75
+ const keepEvents = normalizeKeepEvents(opts.keepEvents);
76
+ const lockPath = `${opts.path}.lock`;
77
+ mkdirSync(dirname(opts.path), { recursive: true });
78
+ acquireLock(lockPath);
79
+ try {
80
+ const active = readActiveStrict(opts.path);
81
+ const archivedRecords = Math.max(0, active.length - keepEvents);
82
+ const prefix = active.slice(0, archivedRecords);
83
+ const tail = active.slice(archivedRecords);
84
+ const firstSeq = prefix[0]?.event.seq ?? null;
85
+ const lastSeq = prefix[prefix.length - 1]?.event.seq ?? null;
86
+ const archiveId = firstSeq === null || lastSeq === null ? null : `${firstSeq}-${lastSeq}`;
87
+ let reusedSegment = false;
88
+ const archivedBySeq = readValidatedArchive(opts.path, active);
89
+ if (archiveId !== null && firstSeq !== null && lastSeq !== null) {
90
+ const archiveDir = rootEventArchiveDir(opts.path);
91
+ const missing = [];
92
+ for (const line of prefix) {
93
+ const archived = archivedBySeq.get(line.event.seq);
94
+ if (archived === undefined) {
95
+ missing.push(line);
96
+ continue;
97
+ }
98
+ if (JSON.stringify(archived) !== JSON.stringify(line.event)) {
99
+ throw new ArchiveSegmentConflictError(String(line.event.seq));
100
+ }
101
+ reusedSegment = true;
102
+ }
103
+ for (const segment of contiguousGroups(missing)) {
104
+ const missingFirstSeq = segment[0].event.seq;
105
+ const missingLastSeq = segment[segment.length - 1].event.seq;
106
+ const archiveBytes = serializeLines(segment);
107
+ mkdirSync(archiveDir, { recursive: true });
108
+ const segmentPath = join(archiveDir, rootEventArchiveSegmentName(missingFirstSeq, missingLastSeq));
109
+ if (existsSync(segmentPath))
110
+ throw new ArchiveSegmentConflictError(`${missingFirstSeq}-${missingLastSeq}`);
111
+ durableReplace(`${segmentPath}.tmp`, segmentPath, archiveBytes);
112
+ fsyncDirectoryBestEffort(archiveDir);
113
+ }
114
+ durableReplace(`${opts.path}.archive.tmp`, opts.path, serializeLines(tail));
115
+ fsyncDirectoryBestEffort(dirname(opts.path));
116
+ }
117
+ return {
118
+ mode: 'write',
119
+ activeRecordsBefore: active.length,
120
+ keepEvents,
121
+ archivedRecords,
122
+ retainedRecords: tail.length,
123
+ firstSeq,
124
+ lastSeq,
125
+ archiveId,
126
+ reusedSegment,
127
+ };
128
+ }
129
+ finally {
130
+ releaseLock(lockPath);
131
+ }
132
+ }
133
+ export function readRootEventHistory(path, archiveDir = rootEventArchiveDir(path)) {
134
+ const bySeq = new Map();
135
+ for (const file of archiveSegmentFiles(archiveDir)) {
136
+ for (const event of readEventsLenient(join(archiveDir, file))) {
137
+ // Archive segments are immutable: the first segment in canonical order owns a seq. A later conflicting
138
+ // segment cannot silently replace it during operational reads. Strict validation still reports the
139
+ // conflict and blocks archive writes.
140
+ if (!bySeq.has(event.seq))
141
+ bySeq.set(event.seq, event);
142
+ }
143
+ }
144
+ // Preserve the pre-archive fail-soft behavior for routine commands. The active log is the current writer
145
+ // source, so it deterministically wins an active/archive conflict without crashing status/report readers.
146
+ for (const event of readEventsLenient(path))
147
+ bySeq.set(event.seq, event);
148
+ return [...bySeq.values()].sort((a, b) => a.seq - b.seq);
149
+ }
150
+ export function validateRootEventHistory(path) {
151
+ const active = readActiveStrict(path);
152
+ readValidatedArchive(path, active);
153
+ }
154
+ export function inspectRootEventArchive(path) {
155
+ const archiveDir = rootEventArchiveDir(path);
156
+ const files = archiveSegmentFiles(archiveDir);
157
+ let bytes = 0;
158
+ let records = 0;
159
+ let invalidLines = 0;
160
+ let firstSeq = null;
161
+ let lastSeq = null;
162
+ for (const file of files) {
163
+ const raw = readFileSync(join(archiveDir, file), 'utf8');
164
+ bytes += Buffer.byteLength(raw, 'utf8');
165
+ for (const line of raw.split('\n')) {
166
+ if (line.length === 0)
167
+ continue;
168
+ try {
169
+ const event = rootEvent.parse(JSON.parse(line));
170
+ firstSeq ??= event.seq;
171
+ lastSeq = event.seq;
172
+ records += 1;
173
+ }
174
+ catch {
175
+ invalidLines += 1;
176
+ }
177
+ }
178
+ }
179
+ return {
180
+ exists: files.length > 0,
181
+ segments: files.length,
182
+ bytes,
183
+ records,
184
+ invalidLines,
185
+ firstSeq,
186
+ lastSeq,
187
+ };
188
+ }
189
+ function readActiveStrict(path) {
190
+ if (!existsSync(path))
191
+ return [];
192
+ const out = [];
193
+ const lines = readFileSync(path, 'utf8').split('\n');
194
+ let previousSeq = null;
195
+ for (let index = 0; index < lines.length; index += 1) {
196
+ const raw = lines[index];
197
+ if (raw.length === 0)
198
+ continue;
199
+ let event;
200
+ try {
201
+ event = rootEvent.parse(JSON.parse(raw));
202
+ }
203
+ catch {
204
+ throw new InvalidRootEventLogError(index + 1, 'invalid json or event schema');
205
+ }
206
+ if (previousSeq !== null && event.seq !== previousSeq + 1) {
207
+ throw new InvalidRootEventLogError(index + 1, 'seq must be contiguous and strictly increasing');
208
+ }
209
+ previousSeq = event.seq;
210
+ out.push({ event, raw });
211
+ }
212
+ return out;
213
+ }
214
+ function readEventsLenient(path) {
215
+ if (!existsSync(path))
216
+ return [];
217
+ const out = [];
218
+ for (const line of readFileSync(path, 'utf8').split('\n')) {
219
+ if (line.length === 0)
220
+ continue;
221
+ try {
222
+ out.push(rootEvent.parse(JSON.parse(line)));
223
+ }
224
+ catch {
225
+ // Active reads retain the existing crash-tail behavior. Strict archive writes refuse these lines.
226
+ }
227
+ }
228
+ return out;
229
+ }
230
+ function archiveSegmentFiles(archiveDir) {
231
+ if (!existsSync(archiveDir))
232
+ return [];
233
+ return readdirSync(archiveDir).filter((entry) => SEGMENT_PATTERN.test(entry)).sort();
234
+ }
235
+ function readArchivedEventsStrict(archiveDir) {
236
+ const out = new Map();
237
+ for (const file of archiveSegmentFiles(archiveDir)) {
238
+ const lines = readFileSync(join(archiveDir, file), 'utf8').split('\n');
239
+ let previousSeq = null;
240
+ for (let index = 0; index < lines.length; index += 1) {
241
+ const line = lines[index];
242
+ if (line.length === 0)
243
+ continue;
244
+ let event;
245
+ try {
246
+ event = rootEvent.parse(JSON.parse(line));
247
+ }
248
+ catch {
249
+ throw new InvalidRootEventArchiveError(file, index + 1);
250
+ }
251
+ if (previousSeq !== null && event.seq !== previousSeq + 1) {
252
+ throw new InvalidRootEventArchiveError(file, index + 1);
253
+ }
254
+ previousSeq = event.seq;
255
+ const current = out.get(event.seq);
256
+ if (current !== undefined && JSON.stringify(current) !== JSON.stringify(event)) {
257
+ throw new ArchiveSegmentConflictError(String(event.seq));
258
+ }
259
+ out.set(event.seq, event);
260
+ }
261
+ }
262
+ return out;
263
+ }
264
+ function readValidatedArchive(path, active) {
265
+ const archived = readArchivedEventsStrict(rootEventArchiveDir(path));
266
+ for (const line of active) {
267
+ const existing = archived.get(line.event.seq);
268
+ if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(line.event)) {
269
+ throw new ArchiveSegmentConflictError(String(line.event.seq));
270
+ }
271
+ }
272
+ assertContinuousHistory(archived, active);
273
+ return archived;
274
+ }
275
+ function assertContinuousHistory(archived, active) {
276
+ const seqs = new Set(archived.keys());
277
+ for (const line of active)
278
+ seqs.add(line.event.seq);
279
+ const ordered = [...seqs].sort((left, right) => left - right);
280
+ if (ordered.length === 0)
281
+ return;
282
+ let expected = 1;
283
+ for (const actual of ordered) {
284
+ if (actual !== expected)
285
+ throw new RootEventHistoryGapError(expected, actual);
286
+ expected += 1;
287
+ }
288
+ }
289
+ function contiguousGroups(lines) {
290
+ const groups = [];
291
+ for (const line of lines) {
292
+ const current = groups[groups.length - 1];
293
+ const previous = current?.[current.length - 1];
294
+ if (current === undefined || previous === undefined || line.event.seq !== previous.event.seq + 1) {
295
+ groups.push([line]);
296
+ }
297
+ else {
298
+ current.push(line);
299
+ }
300
+ }
301
+ return groups;
302
+ }
303
+ function serializeLines(lines) {
304
+ return lines.length === 0 ? '' : `${lines.map((line) => line.raw).join('\n')}\n`;
305
+ }
306
+ function durableReplace(tmpPath, finalPath, contents) {
307
+ let fd = null;
308
+ try {
309
+ fd = openSync(tmpPath, 'w', 0o600);
310
+ writeSync(fd, contents, undefined, 'utf8');
311
+ fsyncSync(fd);
312
+ closeSync(fd);
313
+ fd = null;
314
+ renameSync(tmpPath, finalPath);
315
+ }
316
+ catch (error) {
317
+ if (fd !== null)
318
+ closeSync(fd);
319
+ rmSync(tmpPath, { force: true });
320
+ throw error;
321
+ }
322
+ }
323
+ function fsyncDirectoryBestEffort(path) {
324
+ let fd = null;
325
+ try {
326
+ fd = openSync(path, 'r');
327
+ fsyncSync(fd);
328
+ }
329
+ catch {
330
+ // Windows and some filesystems do not support fsync on directories.
331
+ }
332
+ finally {
333
+ if (fd !== null)
334
+ closeSync(fd);
335
+ }
336
+ }
337
+ function normalizeKeepEvents(value) {
338
+ if (value === undefined)
339
+ return ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS;
340
+ if (!Number.isFinite(value) || value < 1)
341
+ throw new RangeError('keepEvents must be a positive integer');
342
+ return Math.floor(value);
343
+ }
@@ -2,6 +2,7 @@ import { openSync, writeSync, fsyncSync, closeSync, statSync, readSync, mkdirSyn
2
2
  import { dirname } from 'node:path';
3
3
  import { monotonicFactory } from 'ulid';
4
4
  import { acquireLock, releaseLock } from '../util/fileLock.js';
5
+ import { readRootEventHistory } from './eventArchive.js';
5
6
  // Monotonic ULIDs: plain ulid() randomizes the low 80 bits, so two ids minted in the SAME millisecond can sort
6
7
  // out of order — the event log wants time-sortable eventIds (and a flaky CI test proved it). The monotonic
7
8
  // factory increments within a millisecond, guaranteeing each successive eventId is strictly greater.
@@ -73,18 +74,7 @@ export class EventStore {
73
74
  }
74
75
  /** 读全部事件 (跳过尾部半行/损坏行). */
75
76
  readAll() {
76
- if (!existsSync(this.path))
77
- return [];
78
- const out = [];
79
- for (const l of readFileSync(this.path, 'utf8').split('\n')) {
80
- if (!l)
81
- continue;
82
- try {
83
- out.push(rootEvent.parse(JSON.parse(l)));
84
- }
85
- catch { /* 崩溃半行: 跳过 */ }
86
- }
87
- return out;
77
+ return readRootEventHistory(this.path);
88
78
  }
89
79
  *iterate(fromSeq = 0) {
90
80
  for (const e of this.readAll())
@@ -2,7 +2,7 @@ import { EventStore, type EventStoreOptions } from './eventStore.js';
2
2
  import type { RawEvent, RootEvent } from './eventSchema.js';
3
3
  import type { EventSink } from './sink.js';
4
4
  /** 已知事件类型 (军杰 §9; 可 registerEventType 扩展). */
5
- export declare const EVENT_TYPES: readonly ["cycle.started", "cycle.signals_collected", "cycle.solidified", "cycle.failed", "cycle.aborted", "cycle.heartbeat", "cycle.consumed", "decision.gene_selected", "decision.triggered", "decision.suppressed", "personality.selected", "personality.risk_gated", "personality.mutated", "personality.stats_updated", "personality.pivoted", "signals.extracted", "mutation.built", "capsule.produced", "evolution_event.projected", "observer.quarantined", "observer.dead_letter", "actor.human.nudge", "actor.human.intervene", "actor.human.teach", "actor.human.observe", "actor.human.review.approve", "actor.human.review.reject", "gene.distilled", "gene.distill_shadowed", "anti_gene.distilled", "anti_gene.distill_shadowed", "anti_gene.benchmark_result", "anti_gene.rollout_result", "reflection.recorded", "material.batch_ready", "value.reuse_hit", "value.inject", "value.reuse_outcome", "value.recall"];
5
+ export declare const EVENT_TYPES: readonly ["cycle.started", "cycle.signals_collected", "cycle.solidified", "cycle.failed", "cycle.aborted", "cycle.heartbeat", "cycle.consumed", "decision.gene_selected", "decision.triggered", "decision.suppressed", "personality.selected", "personality.risk_gated", "personality.mutated", "personality.stats_updated", "personality.pivoted", "signals.extracted", "mutation.built", "capsule.produced", "evolution_event.projected", "observer.quarantined", "observer.dead_letter", "actor.human.nudge", "actor.human.intervene", "actor.human.teach", "actor.human.observe", "actor.human.review.approve", "actor.human.review.reject", "actor.human.trust.promote", "actor.human.trust.revoke", "actor.human.sidecar.recover", "gene.distilled", "gene.distill_shadowed", "anti_gene.distilled", "anti_gene.distill_shadowed", "anti_gene.benchmark_result", "anti_gene.rollout_result", "reflection.recorded", "material.batch_ready", "value.reuse_hit", "value.inject", "value.reuse_outcome", "value.recall"];
6
6
  export type EventType = (typeof EVENT_TYPES)[number];
7
7
  export declare function registerEventType(t: string): void;
8
8
  export declare function isKnownEventType(t: string): boolean;
@@ -1,4 +1,6 @@
1
+ import { dirname, join } from 'node:path';
1
2
  import { EventStore } from './eventStore.js';
3
+ import { createIssueDraftForEventBestEffort } from '../issueReporter/index.js';
2
4
  /** 已知事件类型 (军杰 §9; 可 registerEventType 扩展). */
3
5
  export const EVENT_TYPES = [
4
6
  'cycle.started', 'cycle.signals_collected', 'cycle.solidified', 'cycle.failed',
@@ -10,6 +12,8 @@ export const EVENT_TYPES = [
10
12
  'observer.quarantined', 'observer.dead_letter',
11
13
  'actor.human.nudge', 'actor.human.intervene', 'actor.human.teach', 'actor.human.observe',
12
14
  'actor.human.review.approve', 'actor.human.review.reject',
15
+ 'actor.human.trust.promote', 'actor.human.trust.revoke',
16
+ 'actor.human.sidecar.recover',
13
17
  'gene.distilled', 'gene.distill_shadowed',
14
18
  'anti_gene.distilled', 'anti_gene.distill_shadowed',
15
19
  'anti_gene.benchmark_result', 'anti_gene.rollout_result',
@@ -49,6 +53,11 @@ export class Ingestor {
49
53
  if (raw.actor?.kind === 'human' && !raw.actor.id)
50
54
  throw new IngestValidationError('actor.kind=human 必带 actor.id (审计, 军杰 §9.7)');
51
55
  const evt = await this.store.append(raw);
56
+ createIssueDraftForEventBestEffort(evt, {
57
+ rootDir: join(dirname(this.store.path), 'issue-reporter'),
58
+ workspaceScope: process.cwd(),
59
+ env: process.env,
60
+ });
52
61
  this.sink?.dispatch(evt);
53
62
  return evt;
54
63
  }
@@ -1,22 +1,22 @@
1
1
  /** ~/.evomap, 可 EVOLVER_HOME/EVOMAP_HOME 覆盖. */
2
- export declare function evomapHome(): string;
3
- export declare function rootEventsPath(): string;
4
- export declare function mvDir(): string;
2
+ export declare function evomapHome(env?: Readonly<Record<string, string | undefined>>): string;
3
+ export declare function rootEventsPath(env?: Readonly<Record<string, string | undefined>>): string;
4
+ export declare function mvDir(env?: Readonly<Record<string, string | undefined>>): string;
5
5
  /** 可进化人格模型持久化文件 (五维向量 + 各键统计 + 变更历史). v1 personality_state.json 的 v2 落点. */
6
- export declare function personalityStatePath(): string;
7
- export declare function assetsDir(): string;
6
+ export declare function personalityStatePath(env?: Readonly<Record<string, string | undefined>>): string;
7
+ export declare function assetsDir(env?: Readonly<Record<string, string | undefined>>): string;
8
8
  /** M1 raw-material substrate (append-only jsonl consumed by the cycle). */
9
- export declare function materialDir(): string;
9
+ export declare function materialDir(env?: Readonly<Record<string, string | undefined>>): string;
10
10
  /** MaterialStore backing file. */
11
- export declare function materialStorePath(): string;
11
+ export declare function materialStorePath(env?: Readonly<Record<string, string | undefined>>): string;
12
12
  /** Per-source watermark cursor used to make re-ingest idempotent (file-level dedup). */
13
- export declare function materialWatermarkPath(): string;
13
+ export declare function materialWatermarkPath(env?: Readonly<Record<string, string | undefined>>): string;
14
14
  /** Proxy LLM-trace day-files dir (`llm-trace-YYYYMMDD.jsonl`), the route-savings source for the value ledger.
15
15
  * Mirrors the proxy's EVOLVER_LLM_TRACE_DIR default so the outreach layer reads what the proxy wrote. */
16
- export declare function tracesDir(): string;
16
+ export declare function tracesDir(env?: Readonly<Record<string, string | undefined>>): string;
17
17
  /** Hub-asset audit trail (`asset_call_log.jsonl`): one JSONL line per hub-asset interaction (search hit/miss,
18
18
  * reuse/reference, publish, review). Written best-effort by AssetCallLog; the desktop's RecallHistory / 召回历程
19
19
  * reads it. Defaults to the evomap home root, beside assets/ + evolution/.
20
20
  * NOTE(reuse/#234): confirm this matches where the proxy/RecallHistory reads — best-effort append, so a path
21
21
  * mismatch degrades the audit line, never the reuse write itself. */
22
- export declare function assetCallLogPath(): string;
22
+ export declare function assetCallLogPath(env?: Readonly<Record<string, string | undefined>>): string;