@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
@@ -1,44 +1,44 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { join } from 'node:path';
3
3
  /** ~/.evomap, 可 EVOLVER_HOME/EVOMAP_HOME 覆盖. */
4
- export function evomapHome() {
5
- return process.env['EVOLVER_HOME'] ?? process.env['EVOMAP_HOME'] ?? join(homedir(), '.evomap');
4
+ export function evomapHome(env = process.env) {
5
+ return env['EVOLVER_HOME'] ?? env['EVOMAP_HOME'] ?? join(homedir(), '.evomap');
6
6
  }
7
- export function rootEventsPath() {
8
- return join(evomapHome(), 'evolution', 'root_events.jsonl');
7
+ export function rootEventsPath(env = process.env) {
8
+ return join(evomapHome(env), 'evolution', 'root_events.jsonl');
9
9
  }
10
- export function mvDir() {
11
- return join(evomapHome(), 'evolution', 'mv');
10
+ export function mvDir(env = process.env) {
11
+ return join(evomapHome(env), 'evolution', 'mv');
12
12
  }
13
13
  /** 可进化人格模型持久化文件 (五维向量 + 各键统计 + 变更历史). v1 personality_state.json 的 v2 落点. */
14
- export function personalityStatePath() {
15
- return join(evomapHome(), 'evolution', 'personality_state.json');
14
+ export function personalityStatePath(env = process.env) {
15
+ return join(evomapHome(env), 'evolution', 'personality_state.json');
16
16
  }
17
- export function assetsDir() {
18
- return join(evomapHome(), 'assets');
17
+ export function assetsDir(env = process.env) {
18
+ return join(evomapHome(env), 'assets');
19
19
  }
20
20
  /** M1 raw-material substrate (append-only jsonl consumed by the cycle). */
21
- export function materialDir() {
22
- return join(evomapHome(), 'evolution', 'material');
21
+ export function materialDir(env = process.env) {
22
+ return join(evomapHome(env), 'evolution', 'material');
23
23
  }
24
24
  /** MaterialStore backing file. */
25
- export function materialStorePath() {
26
- return join(materialDir(), 'material.jsonl');
25
+ export function materialStorePath(env = process.env) {
26
+ return join(materialDir(env), 'material.jsonl');
27
27
  }
28
28
  /** Per-source watermark cursor used to make re-ingest idempotent (file-level dedup). */
29
- export function materialWatermarkPath() {
30
- return join(materialDir(), 'watermark.json');
29
+ export function materialWatermarkPath(env = process.env) {
30
+ return join(materialDir(env), 'watermark.json');
31
31
  }
32
32
  /** Proxy LLM-trace day-files dir (`llm-trace-YYYYMMDD.jsonl`), the route-savings source for the value ledger.
33
33
  * Mirrors the proxy's EVOLVER_LLM_TRACE_DIR default so the outreach layer reads what the proxy wrote. */
34
- export function tracesDir() {
35
- return process.env['EVOLVER_LLM_TRACE_DIR'] ?? join(evomapHome(), 'proxy', 'traces');
34
+ export function tracesDir(env = process.env) {
35
+ return env['EVOLVER_LLM_TRACE_DIR'] ?? join(evomapHome(env), 'proxy', 'traces');
36
36
  }
37
37
  /** Hub-asset audit trail (`asset_call_log.jsonl`): one JSONL line per hub-asset interaction (search hit/miss,
38
38
  * reuse/reference, publish, review). Written best-effort by AssetCallLog; the desktop's RecallHistory / 召回历程
39
39
  * reads it. Defaults to the evomap home root, beside assets/ + evolution/.
40
40
  * NOTE(reuse/#234): confirm this matches where the proxy/RecallHistory reads — best-effort append, so a path
41
41
  * mismatch degrades the audit line, never the reuse write itself. */
42
- export function assetCallLogPath() {
43
- return join(evomapHome(), 'asset_call_log.jsonl');
42
+ export function assetCallLogPath(env = process.env) {
43
+ return join(evomapHome(env), 'asset_call_log.jsonl');
44
44
  }
@@ -5,10 +5,12 @@ export type { Projector } from './replayer.js';
5
5
  export { eventCountsProjector, DEFAULT_PROJECTORS } from './projectors.js';
6
6
  export type { EventCountsMV } from './projectors.js';
7
7
  export { LineTooLargeError, MAX_LINE_BYTES } from './eventStore.js';
8
+ export { ArchiveSegmentConflictError, InvalidRootEventArchiveError, InvalidRootEventLogError, RootEventHistoryGapError, archiveRootEvents, inspectRootEventArchive, planRootEventArchive, readRootEventHistory, rootEventArchiveDir, rootEventArchiveSegmentName, validateRootEventHistory, ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS, } from './eventArchive.js';
9
+ export type { RootEventArchiveOptions, RootEventArchivePlan, RootEventArchiveResult, RootEventArchiveStats, } from './eventArchive.js';
8
10
  export { rootEventsPath, evomapHome, mvDir, personalityStatePath, assetsDir, materialDir, materialStorePath, materialWatermarkPath, tracesDir, assetCallLogPath } from './paths.js';
9
11
  export { EVENT_SCHEMA_VERSION } from './eventSchema.js';
10
12
  export type { RootEvent, RawEvent, HumanNarrative, Actor, Replayability } from './eventSchema.js';
11
13
  export { readEvents, statusReport, listCycles, showCycle, listTriggers, dailySummary, buildNarrativeSnapshot, NARRATIVE_DEFAULT_LIMIT, NARRATIVE_MAX_LIMIT, } from './reports.js';
12
- export { buildRetentionReport, defaultMaterialCursorPath, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
14
+ export { buildRetentionReport, defaultMaterialCursorPath, defaultMaterialCursorPaths, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
13
15
  export type { ReportEvent, StatusReport, CycleSummary, CycleTimeline, TriggerRow, DailySummary, NarrativeEntry, NarrativeOutcome, NarrativeSnapshot, NarrativeSnapshotOptions, } from './reports.js';
14
16
  export type { MaterialRetentionSnapshot, RetentionReport, RetentionReportOptions, RetentionState, RetentionThresholds, RootRetentionSnapshot, } from './retention.js';
@@ -2,7 +2,8 @@ export { Ingestor, EVENT_TYPES, registerEventType, isKnownEventType, IngestValid
2
2
  export { Replayer } from './replayer.js';
3
3
  export { eventCountsProjector, DEFAULT_PROJECTORS } from './projectors.js';
4
4
  export { LineTooLargeError, MAX_LINE_BYTES } from './eventStore.js';
5
+ export { ArchiveSegmentConflictError, InvalidRootEventArchiveError, InvalidRootEventLogError, RootEventHistoryGapError, archiveRootEvents, inspectRootEventArchive, planRootEventArchive, readRootEventHistory, rootEventArchiveDir, rootEventArchiveSegmentName, validateRootEventHistory, ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS, } from './eventArchive.js';
5
6
  export { rootEventsPath, evomapHome, mvDir, personalityStatePath, assetsDir, materialDir, materialStorePath, materialWatermarkPath, tracesDir, assetCallLogPath } from './paths.js';
6
7
  export { EVENT_SCHEMA_VERSION } from './eventSchema.js';
7
8
  export { readEvents, statusReport, listCycles, showCycle, listTriggers, dailySummary, buildNarrativeSnapshot, NARRATIVE_DEFAULT_LIMIT, NARRATIVE_MAX_LIMIT, } from './reports.js';
8
- export { buildRetentionReport, defaultMaterialCursorPath, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
9
+ export { buildRetentionReport, defaultMaterialCursorPath, defaultMaterialCursorPaths, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
@@ -4,7 +4,7 @@ export declare const RETENTION_DEFAULT_MAX_ROOT_BYTES: number;
4
4
  export declare const RETENTION_DEFAULT_MAX_MATERIAL_RECORDS = 10000;
5
5
  export declare const RETENTION_DEFAULT_MAX_MATERIAL_BYTES: number;
6
6
  export declare const RETENTION_DEFAULT_WATCH_RATIO = 0.8;
7
- export declare const RETENTION_DEFAULT_ROOT_TAIL_EVENTS: number;
7
+ export declare const RETENTION_DEFAULT_ROOT_TAIL_EVENTS = 10000;
8
8
  export interface RetentionThresholds {
9
9
  maxRecords: number;
10
10
  maxBytes: number;
@@ -22,6 +22,17 @@ export interface RootRetentionSnapshot {
22
22
  lastSeq: number | null;
23
23
  firstTs: string | null;
24
24
  lastTs: string | null;
25
+ archiveSegments: number;
26
+ archiveRecords: number;
27
+ archiveBytes: number;
28
+ archiveInvalidLines: number;
29
+ historyRecords: number;
30
+ historyBytes: number;
31
+ historyConflicts: number;
32
+ historyGaps: number;
33
+ historyIntegrityErrors: number;
34
+ archiveRotationSupported: true;
35
+ archiveRotationSafe: boolean;
25
36
  protectTailEvents: number;
26
37
  destructivePruneSafe: false;
27
38
  reason: string;
@@ -36,12 +47,22 @@ export interface MaterialRetentionSnapshot {
36
47
  thresholds: RetentionThresholds;
37
48
  firstCapturedAt: string | null;
38
49
  lastCapturedAt: string | null;
50
+ archiveSegments: number;
51
+ archiveRecords: number;
52
+ archiveBytes: number;
53
+ archiveInvalidLines: number;
54
+ historyRecords: number;
55
+ historyBytes: number;
56
+ cursorCount: number;
57
+ minCursor: number;
39
58
  cursorValid: boolean;
40
59
  cursorInRange: boolean;
41
60
  cursor: number;
42
61
  effectiveCursor: number;
43
62
  consumedPrefix: number;
44
63
  pending: number;
64
+ archiveRotationSupported: true;
65
+ archiveRotationSafe: boolean;
45
66
  destructivePruneSafe: false;
46
67
  reason: string;
47
68
  }
@@ -58,6 +79,7 @@ export interface RetentionReportOptions {
58
79
  rootEventsPath?: string;
59
80
  materialStorePath?: string;
60
81
  materialCursorPath?: string;
82
+ materialCursorPaths?: readonly string[];
61
83
  now?: () => number;
62
84
  maxRootEvents?: number;
63
85
  maxRootBytes?: number;
@@ -67,4 +89,5 @@ export interface RetentionReportOptions {
67
89
  protectRootTailEvents?: number;
68
90
  }
69
91
  export declare function defaultMaterialCursorPath(path?: string): string;
92
+ export declare function defaultMaterialCursorPaths(path?: string): string[];
70
93
  export declare function buildRetentionReport(opts?: RetentionReportOptions): RetentionReport;
@@ -1,24 +1,29 @@
1
1
  import { existsSync, readFileSync, statSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { material } from '../schema/material.js';
4
+ import { inspectMaterialArchive, planMaterialArchive, readMaterialHistory, } from '../material/materialArchive.js';
4
5
  import { rootEvent } from './eventSchema.js';
6
+ import { ArchiveSegmentConflictError, inspectRootEventArchive, readRootEventHistory, RootEventHistoryGapError, ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS, validateRootEventHistory, } from './eventArchive.js';
5
7
  import { materialStorePath as defaultMaterialStorePath, rootEventsPath as defaultRootEventsPath } from './paths.js';
6
- import { NARRATIVE_MAX_LIMIT } from './reports.js';
7
8
  export const RETENTION_DEFAULT_MAX_ROOT_EVENTS = 100_000;
8
9
  export const RETENTION_DEFAULT_MAX_ROOT_BYTES = 64 * 1024 * 1024;
9
10
  export const RETENTION_DEFAULT_MAX_MATERIAL_RECORDS = 10_000;
10
11
  export const RETENTION_DEFAULT_MAX_MATERIAL_BYTES = 64 * 1024 * 1024;
11
12
  export const RETENTION_DEFAULT_WATCH_RATIO = 0.8;
12
- export const RETENTION_DEFAULT_ROOT_TAIL_EVENTS = Math.max(1_000, NARRATIVE_MAX_LIMIT);
13
+ export const RETENTION_DEFAULT_ROOT_TAIL_EVENTS = ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS;
13
14
  export function defaultMaterialCursorPath(path = defaultMaterialStorePath()) {
14
15
  return join(dirname(path), 'cycle-consumer.json');
15
16
  }
17
+ export function defaultMaterialCursorPaths(path = defaultMaterialStorePath()) {
18
+ return [defaultMaterialCursorPath(path), join(dirname(path), 'distill-consumer.json')];
19
+ }
16
20
  export function buildRetentionReport(opts = {}) {
17
21
  const now = new Date((opts.now ?? Date.now)()).toISOString();
18
22
  const watchRatio = ratioOf(opts.watchRatio, RETENTION_DEFAULT_WATCH_RATIO);
19
23
  const rootPath = opts.rootEventsPath ?? defaultRootEventsPath();
20
24
  const materialPath = opts.materialStorePath ?? defaultMaterialStorePath();
21
- const cursorPath = opts.materialCursorPath ?? defaultMaterialCursorPath(materialPath);
25
+ const cursorPaths = opts.materialCursorPaths
26
+ ?? (opts.materialCursorPath !== undefined ? [opts.materialCursorPath] : defaultMaterialCursorPaths(materialPath));
22
27
  const rootThresholds = {
23
28
  maxRecords: positiveInt(opts.maxRootEvents, RETENTION_DEFAULT_MAX_ROOT_EVENTS),
24
29
  maxBytes: positiveInt(opts.maxRootBytes, RETENTION_DEFAULT_MAX_ROOT_BYTES),
@@ -30,7 +35,7 @@ export function buildRetentionReport(opts = {}) {
30
35
  watchRatio,
31
36
  };
32
37
  const rootEvents = rootSnapshot(rootPath, rootThresholds, positiveInt(opts.protectRootTailEvents, RETENTION_DEFAULT_ROOT_TAIL_EVENTS));
33
- const material = materialSnapshot(materialPath, cursorPath, materialThresholds);
38
+ const material = materialSnapshot(materialPath, cursorPaths, materialThresholds);
34
39
  const warnings = buildWarnings(rootEvents, material);
35
40
  const nextActions = buildNextActions(rootEvents, material);
36
41
  return {
@@ -45,6 +50,22 @@ export function buildRetentionReport(opts = {}) {
45
50
  }
46
51
  function rootSnapshot(path, thresholds, protectTailEvents) {
47
52
  const stats = readJsonl(path, (value) => rootEvent.parse(value));
53
+ const archive = inspectRootEventArchive(path);
54
+ const historyRecords = readRootEventHistory(path).length;
55
+ let historyConflicts = 0;
56
+ let historyGaps = 0;
57
+ let historyIntegrityErrors = 0;
58
+ try {
59
+ validateRootEventHistory(path);
60
+ }
61
+ catch (error) {
62
+ if (error instanceof ArchiveSegmentConflictError)
63
+ historyConflicts = 1;
64
+ else if (error instanceof RootEventHistoryGapError)
65
+ historyGaps = 1;
66
+ else
67
+ historyIntegrityErrors = 1;
68
+ }
48
69
  return {
49
70
  name: 'root_events',
50
71
  exists: stats.exists,
@@ -57,34 +78,67 @@ function rootSnapshot(path, thresholds, protectTailEvents) {
57
78
  lastSeq: stats.last?.seq ?? null,
58
79
  firstTs: stats.first?.ts ?? null,
59
80
  lastTs: stats.last?.ts ?? null,
81
+ archiveSegments: archive.segments,
82
+ archiveRecords: archive.records,
83
+ archiveBytes: archive.bytes,
84
+ archiveInvalidLines: archive.invalidLines,
85
+ historyRecords,
86
+ historyBytes: archive.bytes + stats.bytes,
87
+ historyConflicts,
88
+ historyGaps,
89
+ historyIntegrityErrors,
90
+ archiveRotationSupported: true,
91
+ archiveRotationSafe: stats.invalidLines === 0 && archive.invalidLines === 0 && historyConflicts === 0 && historyGaps === 0 && historyIntegrityErrors === 0,
60
92
  protectTailEvents: Math.min(stats.records, protectTailEvents),
61
93
  destructivePruneSafe: false,
62
- reason: 'root_events is the replay and audit source; archive readers must exist before destructive prune is safe',
94
+ reason: 'archive rotation preserves replay history; destructive archive deletion remains disabled',
63
95
  };
64
96
  }
65
- function materialSnapshot(path, cursorPath, thresholds) {
97
+ function materialSnapshot(path, cursorPaths, thresholds) {
66
98
  const stats = readJsonl(path, (value) => material.parse(value));
67
- const cursor = readCycleCursor(cursorPath);
68
- const cursorInRange = cursor.value >= 0 && cursor.value <= stats.records;
69
- const effectiveCursor = cursorInRange ? cursor.value : clampCursor(cursor.value, stats.records);
99
+ const archive = inspectMaterialArchive(path);
100
+ const history = readMaterialHistory(path);
101
+ const historyRecords = history.length;
102
+ const cursors = readMaterialCursors(cursorPaths, historyRecords, archive.records);
103
+ const effectiveCursor = cursors.inRange ? cursors.cycle : clampCursor(cursors.cycle, historyRecords);
104
+ const effectiveMinimum = cursors.inRange ? cursors.min : clampCursor(cursors.min, historyRecords);
105
+ let archiveRotationSafe = stats.invalidLines === 0 && archive.invalidLines === 0 && cursors.valid && cursors.inRange;
106
+ if (archiveRotationSafe) {
107
+ try {
108
+ planMaterialArchive({ path, cursorPaths, keepRecords: 1 });
109
+ }
110
+ catch {
111
+ archiveRotationSafe = false;
112
+ }
113
+ }
70
114
  return {
71
115
  name: 'material',
72
- exists: stats.exists,
116
+ exists: stats.exists || archive.exists,
73
117
  bytes: stats.bytes,
74
118
  records: stats.records,
75
119
  invalidLines: stats.invalidLines,
76
120
  state: retentionState(stats.records, stats.bytes, thresholds),
77
121
  thresholds,
78
- firstCapturedAt: stats.first?.capturedAt ?? null,
79
- lastCapturedAt: stats.last?.capturedAt ?? null,
80
- cursorValid: cursor.valid,
81
- cursorInRange,
82
- cursor: cursor.value,
122
+ firstCapturedAt: history[0]?.capturedAt ?? null,
123
+ lastCapturedAt: history[history.length - 1]?.capturedAt ?? null,
124
+ archiveSegments: archive.segments,
125
+ archiveRecords: archive.records,
126
+ archiveBytes: archive.bytes,
127
+ archiveInvalidLines: archive.invalidLines,
128
+ historyRecords,
129
+ historyBytes: archive.bytes + stats.bytes,
130
+ cursorCount: cursors.count,
131
+ minCursor: effectiveMinimum,
132
+ cursorValid: cursors.valid,
133
+ cursorInRange: cursors.inRange,
134
+ cursor: cursors.cycle,
83
135
  effectiveCursor,
84
- consumedPrefix: effectiveCursor,
85
- pending: Math.max(0, stats.records - effectiveCursor),
136
+ consumedPrefix: effectiveMinimum,
137
+ pending: Math.max(0, historyRecords - effectiveCursor),
138
+ archiveRotationSupported: true,
139
+ archiveRotationSafe,
86
140
  destructivePruneSafe: false,
87
- reason: 'material cursor is index-based; prune requires atomic archive plus cursor rewrite',
141
+ reason: 'archive rotation preserves absolute consumer cursors and full material history; destructive archive deletion remains disabled',
88
142
  };
89
143
  }
90
144
  function readJsonl(path, parse) {
@@ -111,19 +165,49 @@ function readJsonl(path, parse) {
111
165
  }
112
166
  return { exists: true, bytes, records, invalidLines, first, last };
113
167
  }
114
- function readCycleCursor(path) {
115
- if (!existsSync(path))
116
- return { exists: false, valid: true, value: 0 };
117
- try {
118
- const parsed = JSON.parse(readFileSync(path, 'utf8'));
119
- if (typeof parsed.cycle === 'number' && Number.isFinite(parsed.cycle)) {
120
- return { exists: true, valid: true, value: Math.floor(parsed.cycle) };
168
+ function readMaterialCursors(paths, historyRecords, archiveRecords) {
169
+ let valid = true;
170
+ let inRange = true;
171
+ let cycle = 0;
172
+ let cycleFound = false;
173
+ const values = [];
174
+ for (const path of paths) {
175
+ if (!existsSync(path))
176
+ continue;
177
+ let parsed;
178
+ try {
179
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
180
+ }
181
+ catch {
182
+ valid = false;
183
+ continue;
184
+ }
185
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
186
+ valid = false;
187
+ continue;
188
+ }
189
+ for (const [group, value] of Object.entries(parsed)) {
190
+ if (!Number.isSafeInteger(value) || value < 0) {
191
+ valid = false;
192
+ continue;
193
+ }
194
+ const position = value;
195
+ values.push(position);
196
+ if (position > historyRecords)
197
+ inRange = false;
198
+ if (group === 'cycle' && !cycleFound) {
199
+ cycle = position;
200
+ cycleFound = true;
201
+ }
121
202
  }
122
- return { exists: true, valid: false, value: 0 };
123
- }
124
- catch {
125
- return { exists: true, valid: false, value: 0 };
126
203
  }
204
+ return {
205
+ valid,
206
+ inRange,
207
+ count: values.length,
208
+ cycle,
209
+ min: values.length === 0 ? archiveRecords : Math.min(...values),
210
+ };
127
211
  }
128
212
  function retentionState(records, bytes, thresholds) {
129
213
  if (records > thresholds.maxRecords || bytes > thresholds.maxBytes)
@@ -136,30 +220,42 @@ function buildWarnings(root, mat) {
136
220
  const out = [];
137
221
  if (root.invalidLines > 0)
138
222
  out.push(`root_events has ${root.invalidLines} invalid jsonl line(s); recover before rotating`);
223
+ if (root.archiveInvalidLines > 0)
224
+ out.push(`root_events archive has ${root.archiveInvalidLines} invalid jsonl line(s); repair archive integrity before rotating again`);
225
+ if (root.historyConflicts > 0)
226
+ out.push('root_events active/archive history has conflicting seq records; repair audit integrity before rotating');
227
+ if (root.historyGaps > 0)
228
+ out.push('root_events active/archive history has a seq gap; restore the missing audit segment before rotating');
229
+ if (root.historyIntegrityErrors > 0)
230
+ out.push('root_events history fails structural integrity checks; repair event ordering before rotating');
139
231
  if (mat.invalidLines > 0)
140
232
  out.push(`material store has ${mat.invalidLines} invalid jsonl line(s); inspect before rotating`);
141
233
  if (!mat.cursorValid)
142
234
  out.push('material cursor is unreadable; retention uses effectiveCursor=0 until the cursor is repaired');
143
235
  if (mat.cursorValid && !mat.cursorInRange)
144
- out.push(`material cursor ${mat.cursor} is outside material record range 0..${mat.records}; retention uses effectiveCursor=${mat.effectiveCursor}`);
236
+ out.push(`material cursor ${mat.cursor} is outside material record range 0..${mat.historyRecords}; retention uses effectiveCursor=${mat.effectiveCursor}`);
145
237
  if (root.state === 'over_limit')
146
- out.push('root_events exceeds retention thresholds; plan archive support before deleting audit lines');
238
+ out.push('root_events exceeds retention thresholds for the active log; preview archive rotation before writing');
239
+ if (mat.archiveInvalidLines > 0)
240
+ out.push(`material archive has ${mat.archiveInvalidLines} invalid jsonl line(s); repair archive integrity before rotating again`);
241
+ if (!mat.archiveRotationSafe)
242
+ out.push('material active/archive/cursor state is not safe for rotation; run archive-material preview after repairing integrity');
147
243
  if (mat.state === 'over_limit')
148
- out.push('material store exceeds retention thresholds; archive consumed prefix only with cursor rewrite');
244
+ out.push('material active store exceeds retention thresholds; preview absolute-cursor archive rotation before writing');
149
245
  if (mat.pending > 0)
150
246
  out.push(`material store has ${mat.pending} pending record(s); do not prune pending material`);
151
247
  return out;
152
248
  }
153
249
  function buildNextActions(root, mat) {
154
250
  const out = [
155
- 'keep destructive prune disabled until archive readers are wired into reports, replay and cycle recovery',
251
+ 'archive rotation is available; keep destructive prune disabled for archive deletion',
156
252
  ];
157
253
  if (root.state !== 'ok')
158
- out.push(`root_events ${root.state}: keep at least the last ${root.protectTailEvents} event(s) active after future archive`);
254
+ out.push(`root_events ${root.state}: run retention archive-root preview, then keep at least the last ${root.protectTailEvents} active event(s)`);
159
255
  if (!mat.cursorValid || !mat.cursorInRange)
160
- out.push('material: repair the cycle cursor before archiving consumed material');
161
- if (mat.consumedPrefix > 0)
162
- out.push(`material: ${mat.consumedPrefix} consumed prefix record(s) are archive candidates after atomic cursor rewrite exists`);
256
+ out.push('material: repair the cycle cursor and any other durable consumer cursor before archiving consumed material');
257
+ if (mat.consumedPrefix > mat.archiveRecords)
258
+ out.push(`material: ${mat.consumedPrefix - mat.archiveRecords} consumed prefix record(s) may be archived with archive-material without rewriting cursors`);
163
259
  if (mat.pending > 0)
164
260
  out.push('material: process or inspect pending records before any future prune');
165
261
  return out;
@@ -2,13 +2,14 @@ import type { AssetStoreProvider } from '../assetstore/provider.js';
2
2
  import { type ProvenanceStore } from '../assetstore/provenance.js';
3
3
  import type { ReviewLedger } from '../assetstore/reviewLedger.js';
4
4
  import type { GeneCandidateInput } from '../algo/geneSelection.js';
5
- import type { CycleEngine, SolidifyPermitGate } from '../algo/cycleEngine.js';
5
+ import type { CycleEngine, ExecutionFailureKind, SolidifyPermitGate } from '../algo/cycleEngine.js';
6
6
  import { type AutonomousSafety } from './autonomousCycle.js';
7
7
  import type { GitRunner, ValidateHook } from './claudeBridge.js';
8
8
  import type { AgentRunner } from './runnerRegistry.js';
9
9
  import { type OpenPrLister } from './openPrRegistry.js';
10
10
  import type { ReuseOutcomeSummary, ReuseOutcomeEvent } from '../ops/reuseOutcomes.js';
11
11
  import type { PersonalityStore } from '../personality/store.js';
12
+ import type { MemoryGraphProvider } from '../algo/memoryGraph.js';
12
13
  export interface AutoExecTask {
13
14
  id: string;
14
15
  repo: string;
@@ -39,6 +40,8 @@ export interface AutoExecVerdict {
39
40
  score: number;
40
41
  };
41
42
  proofOfWork?: unknown;
43
+ failureKind?: ExecutionFailureKind;
44
+ exitCode?: number | null;
42
45
  usedAssetIds?: readonly string[];
43
46
  }
44
47
  /**
@@ -97,6 +100,8 @@ export interface AutoExecDeps {
97
100
  /** Observed `value.recall` events (#274 slice 3): folded into the same soft re-order as reuseOutcomes (lower
98
101
  * weight) so transcript-observed recall influences selection. Forwarded to runEvolutionCycle. Omit → none. */
99
102
  recallEvents?: readonly ReuseOutcomeEvent[];
103
+ /** Scoped local MemoryGraph seam. Queries and records structured outcome data only. */
104
+ memoryGraph?: MemoryGraphProvider;
100
105
  /** Optional daemon-level explicit strategy preset name, e.g. EVOLVE_STRATEGY. */
101
106
  strategyName?: string;
102
107
  /** Optional evolvable personality store shared with CycleEngine and the exec prompt. */
@@ -122,6 +122,9 @@ export async function runAutoExecTask(deps, rawTask, safety) {
122
122
  const gi = intakeGene({
123
123
  category: 'repair', signals_match: cycleSignals, strategy: [...task.strategy],
124
124
  summary: task.expectedEffect.slice(0, 80), ...(task.validationCmds ? { validation: [...task.validationCmds] } : {}),
125
+ // A task's seeded strategy is an execution-entry seed (the operator/daemon supplied a learned strategy to
126
+ // run/evolve), not a skill/session transcription → `evolved` per V1 #302 classifyProvenance.
127
+ generation_meta: { source: 'evolved' },
125
128
  });
126
129
  if (gi.ok && gi.gene) {
127
130
  await deps.store.put(gi.gene);
@@ -158,6 +161,15 @@ export async function runAutoExecTask(deps, rawTask, safety) {
158
161
  // The forced pick still passes every hard gate downstream (candidate pool, ban, epigenetic
159
162
  // suppression), so this never bypasses trust/review/inert filtering.
160
163
  const cycleForcedGeneId = task.forcedGeneId ?? seededStrategyGeneId;
164
+ let memoryGraphAdvice;
165
+ if (deps.memoryGraph) {
166
+ try {
167
+ memoryGraphAdvice = await deps.memoryGraph.query({ workspace: task.repo, signals: cycleSignals });
168
+ }
169
+ catch {
170
+ memoryGraphAdvice = undefined;
171
+ }
172
+ }
161
173
  const res = await runEvolutionCycle(deps.engine, deps.store, {
162
174
  ...(deps.provenance ? { provenance: deps.provenance } : {}),
163
175
  ...(deps.review ? { review: deps.review } : {}),
@@ -166,6 +178,7 @@ export async function runAutoExecTask(deps, rawTask, safety) {
166
178
  ...(deps.solidifyPermit ? { solidifyPermit: deps.solidifyPermit } : {}),
167
179
  ...(deps.reuseOutcomes ? { reuseOutcomes: deps.reuseOutcomes } : {}),
168
180
  ...(deps.recallEvents ? { recallEvents: deps.recallEvents } : {}),
181
+ ...(memoryGraphAdvice ? { memoryGraphAdvice } : {}),
169
182
  ...(strategyName !== undefined ? { strategyName } : {}),
170
183
  ...(cycleForcedGeneId !== undefined ? { forcedGeneId: cycleForcedGeneId } : {}),
171
184
  cycleId,
@@ -181,6 +194,25 @@ export async function runAutoExecTask(deps, rawTask, safety) {
181
194
  });
182
195
  const status = res.finalStage === 'solidified' ? 'solidified' : res.finalStage === 'failed' ? 'failed' : 'innovated';
183
196
  const cap = res.capsule;
197
+ if (deps.memoryGraph && res.decision?.selectedGeneId && (res.finalStage === 'solidified' || res.finalStage === 'failed')) {
198
+ const producedSuccess = res.finalStage === 'solidified' && res.producedValue === true;
199
+ try {
200
+ await deps.memoryGraph.recordOutcome({
201
+ workspace: task.repo,
202
+ signals: cycleSignals,
203
+ geneId: res.decision.selectedGeneId,
204
+ // MemoryGraph has no inert status yet, so record a no-op as conservative failed evidence instead of reward.
205
+ status: producedSuccess ? 'success' : 'failed',
206
+ score: res.finalStage === 'solidified' && !res.producedValue
207
+ ? 0
208
+ : cap?.outcome?.score ?? (producedSuccess ? 1 : 0),
209
+ at: new Date().toISOString(),
210
+ });
211
+ }
212
+ catch {
213
+ // Memory persistence is advisory and must never fail the autonomous task.
214
+ }
215
+ }
184
216
  const hubAssetIds = new Set(hubCandidates.map((c) => c.assetId).filter((id) => typeof id === 'string' && id.length > 0));
185
217
  const selectedAssetId = res.decision?.selectedAssetId;
186
218
  const usedAssetIds = selectedAssetId && hubAssetIds.has(selectedAssetId) ? [selectedAssetId] : [];
@@ -189,6 +221,8 @@ export async function runAutoExecTask(deps, rawTask, safety) {
189
221
  ...(res.reasons.length > 0 && status !== 'solidified' ? { reason: res.reasons.join('; ') } : {}),
190
222
  ...(cap?.outcome ? { outcome: cap.outcome } : {}),
191
223
  ...(cap?.proof_of_work ? { proofOfWork: cap.proof_of_work } : {}),
224
+ ...(res.failureKind !== undefined ? { failureKind: res.failureKind } : {}),
225
+ ...(res.exitCode !== undefined ? { exitCode: res.exitCode } : {}),
192
226
  ...(usedAssetIds.length > 0 ? { usedAssetIds } : {}),
193
227
  };
194
228
  }
@@ -29,6 +29,8 @@ export interface AutonomousSafety {
29
29
  /** Only embed trusted gene strategies (#45). Default true. */
30
30
  requireTrustedGene?: boolean;
31
31
  timeoutMs?: number;
32
+ /** Cooperative cancellation propagated to the spawned runner process tree. */
33
+ signal?: AbortSignal;
32
34
  }
33
35
  /**
34
36
  * Build the fully-hardened `execute` for an autonomous run against `repo`. Composes every exec-bridge control
@@ -43,12 +43,16 @@ const CODEX_DEFAULT_AGENT_OPTIONS = {};
43
43
  // auto-approve shell+write, but Cursor has no verified per-run allowlist/sandbox mapping yet, so skipPermissions
44
44
  // is refused outright. Use default cursor with worktree isolation until the CLI is run-verified.
45
45
  const CURSOR_DEFAULT_AGENT_OPTIONS = {};
46
+ // Gemini's verified safe default is `--approval-mode auto_edit`; shell remains gated and --yolo is refused.
47
+ const GEMINI_DEFAULT_AGENT_OPTIONS = {};
46
48
  /** Per-runner safe default agent options — claude bypasses-with-bounds; codex + cursor stay non-bypassing (codex sandboxed; cursor skip refused, #66). */
47
49
  function defaultAgentOptions(runner) {
48
50
  if (runner === 'codex')
49
51
  return CODEX_DEFAULT_AGENT_OPTIONS;
50
52
  if (runner === 'cursor')
51
53
  return CURSOR_DEFAULT_AGENT_OPTIONS;
54
+ if (runner === 'gemini')
55
+ return GEMINI_DEFAULT_AGENT_OPTIONS;
52
56
  return CLAUDE_DEFAULT_AGENT_OPTIONS;
53
57
  }
54
58
  /**
@@ -70,6 +74,7 @@ export function makeSafeExecute(repo, store, safety, opts = {}) {
70
74
  requireTrustedGene: safety.requireTrustedGene ?? true,
71
75
  agentOptions: safety.agentOptions ?? defaultAgentOptions(safety.runner),
72
76
  ...(safety.timeoutMs !== undefined ? { timeoutMs: safety.timeoutMs } : {}),
77
+ ...(safety.signal ? { signal: safety.signal } : {}),
73
78
  resolveGene: makeTrustedGeneResolver(store, opts.provenance, opts.review, opts.includeProbation ?? false),
74
79
  ...(opts.validate ? { validate: opts.validate } : {}),
75
80
  ...(opts.validationCmds ? { validationCmds: opts.validationCmds } : {}),
@@ -4,10 +4,16 @@ import type { ExecutionResult } from '../algo/cycleEngine.js';
4
4
  import { type GeneStrategyInfo } from './prompt.js';
5
5
  import type { PersonalityStore } from '../personality/store.js';
6
6
  import { type AgentRunner, type AgentRunnerOptions, type RunnerName } from './runnerRegistry.js';
7
- export { resolveSpawnCommand, spawnCapture, UnboundedSkipPermissionsError, UnsupportedCursorSkipPermissionsError, claudeRunnerArgs, makeClaudeHeadlessRunner, claudeHeadlessRunner, codexRunnerArgs, makeCodexHeadlessRunner, cursorRunnerArgs, makeCursorHeadlessRunner, getRunnerSpec, } from './runnerRegistry.js';
7
+ export { resolveSpawnCommand, spawnCapture, DEFAULT_MAX_CAPTURE_BYTES, UnboundedSkipPermissionsError, UnsupportedCursorSkipPermissionsError, UnsupportedGeminiPermissionOptionsError, claudeRunnerArgs, makeClaudeHeadlessRunner, claudeHeadlessRunner, codexRunnerArgs, makeCodexHeadlessRunner, cursorRunnerArgs, makeCursorHeadlessRunner, getRunnerSpec, geminiRunnerArgs, makeGeminiHeadlessRunner, classifyGeminiRunnerResult, } from './runnerRegistry.js';
8
8
  export type { AgentRunContext, AgentRunResult, AgentRunner, RunnerName, AgentRunnerOptions, ClaudeRunnerOptions, CodexRunnerOptions, AgentRunnerSpec, } from './runnerRegistry.js';
9
+ export interface GitRunnerOptions {
10
+ processSignalMode?: 'cancel' | 'ignore';
11
+ }
9
12
  /** Run a git subcommand in cwd and return its stdout. */
10
- export type GitRunner = (args: readonly string[], cwd: string) => Promise<string>;
13
+ export type GitRunner = (args: readonly string[], cwd: string, signal?: AbortSignal, options?: GitRunnerOptions) => Promise<string>;
14
+ export type GitPatchWriter = (args: readonly string[], cwd: string, destination: string, signal?: AbortSignal,
15
+ /** Call immediately after this writer exclusively creates `destination`. */
16
+ onDestinationOpened?: () => void) => Promise<void>;
11
17
  /** Resolve the selected gene's learned strategy (for prompt enrichment). */
12
18
  export type GeneResolver = (geneId: string) => Promise<GeneStrategyInfo | null> | GeneStrategyInfo | null;
13
19
  /** Decide success from the post-run working tree (e.g. run the gene's validation plan). */
@@ -29,6 +35,12 @@ export interface ExecBridgeOptions {
29
35
  agentOptions?: AgentRunnerOptions;
30
36
  /** Default: spawns `git`. Inject a fake in tests. */
31
37
  git?: GitRunner;
38
+ /** Optional complete-patch sink. The default streams built-in git output directly to disk. */
39
+ gitPatchWriter?: GitPatchWriter;
40
+ /** Test seam for fallback patch persistence; production callers should use the exclusive private writer. */
41
+ writePatchFile?: (path: string, patch: string) => void;
42
+ /** Test seam for temporary patch cleanup; production callers should use the filesystem default. */
43
+ removePatchFile?: (path: string) => void;
32
44
  /** Default: EVOLVE_EXEC_BRIDGE === '1'. Set true to force-enable (e.g. integration tests). */
33
45
  enabled?: boolean;
34
46
  /**
@@ -57,6 +69,8 @@ export interface ExecBridgeOptions {
57
69
  requireTrustedGene?: boolean;
58
70
  /** Per-run agent timeout. Default 600_000ms (10 min). */
59
71
  timeoutMs?: number;
72
+ /** Cooperative cancellation propagated to the runner process tree. */
73
+ signal?: AbortSignal;
60
74
  /** Optional: enrich the prompt with the selected gene's strategy. */
61
75
  resolveGene?: GeneResolver;
62
76
  /** Optional: validation commands surfaced in the prompt's done-criteria. */
@@ -94,6 +108,9 @@ export declare class ExecBridgeForbiddenError extends Error {
94
108
  export declare class UnsandboxedFullAccessRequiresIsolationError extends Error {
95
109
  constructor();
96
110
  }
111
+ export declare class UnsafeWorktreePathError extends Error {
112
+ constructor(reason: string);
113
+ }
97
114
  /**
98
115
  * Whitelist-filter `env` for a spawned agent/tool: keep ONLY the minimal runtime env + the caller-declared
99
116
  * extras (the runner's own auth via allowPrefixes/allowKeys); drop everything else. Fail-safe by construction —
@@ -103,8 +120,9 @@ export declare function scrubAgentEnv(env: NodeJS.ProcessEnv, opts?: {
103
120
  allowKeys?: readonly string[];
104
121
  allowPrefixes?: readonly string[];
105
122
  }): NodeJS.ProcessEnv;
106
- /** Default git runner: spawn `git <args>` in cwd, return stdout (empty string on error). Env scrubbed — git never needs evolver/hub secrets. */
123
+ /** Default git runner: every incomplete command result fails closed. */
107
124
  export declare const defaultGitRunner: GitRunner;
125
+ export declare const defaultGitPatchWriter: GitPatchWriter;
108
126
  /**
109
127
  * Build the `execute` function CycleEngine/runEvolutionCycle consume. Default-off: throws
110
128
  * ExecBridgeDisabledError on first call unless enabled.