newmark-agent 0.6.3 → 0.6.7

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 (72) hide show
  1. package/config.example.json +13 -3
  2. package/dist/cli-commands.d.ts +1 -1
  3. package/dist/cli-commands.js +53 -2
  4. package/dist/cli-discovery.js +3 -0
  5. package/dist/conversation-utility-host.bundle.cjs +3046 -1038
  6. package/dist/conversation-utility-host.js +94 -7
  7. package/dist/core/agent.d.ts +149 -12
  8. package/dist/core/agent.js +843 -176
  9. package/dist/core/agentKernelRunner.d.ts +7 -1
  10. package/dist/core/agentKernelRunner.js +171 -22
  11. package/dist/core/autoRouter.d.ts +49 -44
  12. package/dist/core/autoRouter.js +117 -302
  13. package/dist/core/branchIdentity.d.ts +29 -0
  14. package/dist/core/branchIdentity.js +54 -0
  15. package/dist/core/config.js +27 -11
  16. package/dist/core/continuation/contracts.d.ts +1 -1
  17. package/dist/core/continuation/store.d.ts +119 -1
  18. package/dist/core/continuation/store.js +337 -19
  19. package/dist/core/conversationKernel.d.ts +62 -8
  20. package/dist/core/conversationKernel.js +361 -47
  21. package/dist/core/conversationStateDocument.d.ts +18 -0
  22. package/dist/core/conversationStateDocument.js +88 -0
  23. package/dist/core/conversationVisualBudget.d.ts +74 -0
  24. package/dist/core/conversationVisualBudget.js +153 -0
  25. package/dist/core/electronUtilityAgentClient.d.ts +3 -2
  26. package/dist/core/electronUtilityAgentClient.js +32 -8
  27. package/dist/core/electronUtilityRuntimePool.d.ts +28 -3
  28. package/dist/core/electronUtilityRuntimePool.js +93 -24
  29. package/dist/core/hostRuntimeHooks.d.ts +23 -0
  30. package/dist/core/hostRuntimeHooks.js +17 -0
  31. package/dist/core/jevDecision.d.ts +144 -0
  32. package/dist/core/jevDecision.js +226 -0
  33. package/dist/core/memoryProbe.d.ts +24 -0
  34. package/dist/core/memoryProbe.js +104 -0
  35. package/dist/core/mobilePairing.d.ts +7 -1
  36. package/dist/core/mobilePairing.js +9 -1
  37. package/dist/core/performanceDiagnostics.d.ts +1 -1
  38. package/dist/core/routeDecisionValidator.d.ts +86 -0
  39. package/dist/core/routeDecisionValidator.js +249 -0
  40. package/dist/core/routeEligibility.d.ts +52 -0
  41. package/dist/core/routeEligibility.js +108 -0
  42. package/dist/core/runtimeMemoryBudget.d.ts +6 -0
  43. package/dist/core/runtimeMemoryBudget.js +12 -0
  44. package/dist/core/utilityAgentProtocol.d.ts +8 -10
  45. package/dist/core/utilityHostToolRouter.js +2 -0
  46. package/dist/core/visualDownscale.d.ts +6 -0
  47. package/dist/core/visualDownscale.js +137 -0
  48. package/dist/core/wslAgentClient.d.ts +1 -2
  49. package/dist/core/wslAgentClient.js +0 -8
  50. package/dist/core/wslAgentProtocol.d.ts +1 -10
  51. package/dist/core/wslAgentRuntimePool.d.ts +1 -3
  52. package/dist/core/wslAgentRuntimePool.js +17 -22
  53. package/dist/llm/provider.d.ts +3 -1
  54. package/dist/llm/provider.js +53 -9
  55. package/dist/main.js +108 -31
  56. package/dist/preload.js +3 -1
  57. package/dist/server.js +30 -18
  58. package/dist/tools/computerUse.d.ts +4 -0
  59. package/dist/tools/computerUse.js +202 -4
  60. package/dist/tools/computerUsePowerShellHost.d.ts +6 -1
  61. package/dist/tools/computerUsePowerShellHost.js +105 -31
  62. package/dist/tools/index.js +4 -1
  63. package/dist/tui/src/adapters/core-runtime-adapter.js +13 -1
  64. package/dist/tui/src/i18n.js +12 -1
  65. package/dist/tui/src/render.js +9 -2
  66. package/dist/tui/src/settings-schema.js +19 -0
  67. package/dist/tui/src/state.js +69 -1
  68. package/dist/ui/index.html +994 -277
  69. package/dist/ui/lucide-sprite.svg +0 -8
  70. package/dist/wsl-agent-host.bundle.cjs +2851 -1013
  71. package/dist/wsl-agent-host.js +0 -3
  72. package/package.json +38 -9
@@ -7,6 +7,8 @@ const conversationTarget_1 = require("./core/conversationTarget");
7
7
  const utilityHostToolBridge_1 = require("./core/utilityHostToolBridge");
8
8
  const terminalTakeover_1 = require("./tools/terminalTakeover");
9
9
  const runtimeLifecycle_1 = require("./core/runtimeLifecycle");
10
+ const memoryProbe_1 = require("./core/memoryProbe");
11
+ const workEventCoalescer_1 = require("./core/workEventCoalescer");
10
12
  const root = String(process.env.NEWMARK_RUNTIME_ROOT || '');
11
13
  const expectedRuntimeKey = String(process.env.NEWMARK_RUNTIME_KEY || '');
12
14
  if (!root)
@@ -17,6 +19,11 @@ const parentPort = process.parentPort;
17
19
  if (!parentPort)
18
20
  throw new Error('Electron utility parentPort is unavailable');
19
21
  const host = new agent_1.Agent(root, { runtimeLifecycleRole: 'utility' });
22
+ /**
23
+ * Diagnostic probe (source-only, env-gated). `memoryProbe.disabled.ts` replaces
24
+ * this module in packaged builds, so no probe code ships in the final artifact.
25
+ */
26
+ const hostHooks = (0, memoryProbe_1.installHostRuntimeHooks)({ root });
20
27
  host.tools.setHostProfile({
21
28
  kind: 'electron-utility',
22
29
  platform: process.platform,
@@ -26,9 +33,35 @@ host.tools.setHostProfile({
26
33
  const kernel = new conversationKernel_1.ConversationKernel(root, host, null);
27
34
  let activeTarget = null;
28
35
  function post(value) {
29
- parentPort.postMessage(value);
36
+ hostHooks.countPosted(value);
37
+ try {
38
+ parentPort.postMessage(value);
39
+ }
40
+ catch { /* the parent may already be gone during shutdown */ }
41
+ }
42
+ /**
43
+ * dev-0.6.6 long-run stability: coalesce response/thought deltas **inside the
44
+ * host** before they cross the process boundary.
45
+ *
46
+ * Measured defect: the host posted one IPC message per model delta (18,746
47
+ * messages / 722 MiB for ~31 MB of model text — a 23x amplification) and the
48
+ * heap snapshot showed 10,728 retained per-delta strings. The main process
49
+ * already coalesces for the renderer (`main.ts` WorkEventCoalescer) and the
50
+ * renderer expands `coalescedDeltas` back into individual deltas, so batching at
51
+ * the host boundary changes nothing the user or the transcript sees — it only
52
+ * stops the per-delta pile-up. Lifecycle/tool events still flush immediately and
53
+ * keep their ordering.
54
+ */
55
+ const hostWorkCoalescer = new workEventCoalescer_1.WorkEventCoalescer(event => post({ event: 'work', data: event }), Math.max(4, Number(process.env.NEWMARK_HOST_COALESCE_MS) || 16));
56
+ function flushHostWorkEvents() {
57
+ try {
58
+ hostWorkCoalescer.flushAll();
59
+ }
60
+ catch { /* shutdown best effort */ }
30
61
  }
31
- kernel.subscribe(event => post({ event: 'work', data: event }));
62
+ process.on('exit', flushHostWorkEvents);
63
+ process.on('beforeExit', flushHostWorkEvents);
64
+ kernel.subscribe(event => hostWorkCoalescer.push(event));
32
65
  function checkedTarget(target) {
33
66
  const normalized = (0, conversationTarget_1.normalizeConversationTarget)(target);
34
67
  if (normalized.runtimeKey !== expectedRuntimeKey) {
@@ -37,6 +70,61 @@ function checkedTarget(target) {
37
70
  activeTarget = normalized;
38
71
  return normalized;
39
72
  }
73
+ /**
74
+ * dev-0.6.6 long-run memory reclamation: the transcript part of a snapshot is
75
+ * only sent when the caller cannot already prove it holds that exact revision.
76
+ *
77
+ * Measured before this: RPC responses (mostly snapshots) were **86% of all
78
+ * cross-process bytes** in a 12-round load (24 responses = 329 MiB, single
79
+ * response up to 38 MB) because every poll re-serialized the whole conversation.
80
+ *
81
+ * The handshake is caller-driven: a slim payload is only produced when the
82
+ * caller supplies the revision it currently holds. A caller that holds nothing
83
+ * (or painted another conversation) receives the full transcript; the main
84
+ * process only attaches a revision it received from that same renderer through
85
+ * `conversation:ackTranscript`.
86
+ */
87
+ const transcriptRevisions = new Map();
88
+ function transcriptRevisionOf(snapshot) {
89
+ const messages = Array.isArray(snapshot.chatMessages) ? snapshot.chatMessages : [];
90
+ const runs = Array.isArray(snapshot.workRuns) ? snapshot.workRuns : [];
91
+ const last = messages.length ? messages[messages.length - 1] : null;
92
+ const lastRun = runs.length ? runs[runs.length - 1] : null;
93
+ // Only what the renderer actually paints takes part in the revision: the
94
+ // visible message window and the work-run summary. `history` deliberately
95
+ // does NOT: it grows on every tool round inside a turn, and folding it in made
96
+ // every mid-turn poll look "changed" (measured: no slim snapshots at all).
97
+ // Anything derived from `history` is only recomputed when the window changes,
98
+ // which is exactly when a full payload is sent anyway.
99
+ return [
100
+ messages.length,
101
+ String(last?.messageId || last?.clientMessageId || last?.timestamp || ''),
102
+ String(typeof last?.content === 'string' ? last.content.length : ''),
103
+ String(snapshot.totalMessages ?? ''),
104
+ String(snapshot.windowStart ?? ''),
105
+ runs.length,
106
+ String(lastRun?.status || ''),
107
+ String(lastRun?.endedAt || ''),
108
+ ].join('|');
109
+ }
110
+ function applyTranscriptPolicy(snapshot, options) {
111
+ const revision = transcriptRevisionOf(snapshot);
112
+ const key = `${snapshot.target?.workspaceId || ''}::${snapshot.target?.conversationId || ''}`;
113
+ transcriptRevisions.set(key, revision);
114
+ const callerRevision = String(options.transcriptRevision || '');
115
+ const full = options.transcript === 'force' || !callerRevision || callerRevision !== revision;
116
+ if (full)
117
+ return { ...snapshot, transcriptRevision: revision };
118
+ return {
119
+ ...snapshot,
120
+ chatMessages: undefined,
121
+ history: undefined,
122
+ workRuns: undefined,
123
+ workEvents: undefined,
124
+ transcriptUnchanged: true,
125
+ transcriptRevision: revision,
126
+ };
127
+ }
40
128
  (0, utilityHostToolBridge_1.configureUtilityHostToolBridge)(request => post({ event: 'host_tool_request', data: request }), () => activeTarget ? {
41
129
  workspaceId: activeTarget.workspaceId,
42
130
  conversationId: activeTarget.conversationId,
@@ -64,8 +152,10 @@ async function handle(request) {
64
152
  const result = await kernel.prompt(request.params.message, target, request.params.options, request.params.queueMode);
65
153
  return { ...result, backend: 'utility', pid: process.pid };
66
154
  }
67
- if (request.method === 'snapshot')
68
- return kernel.snapshot(checkedTarget(request.params.target), request.params.options);
155
+ if (request.method === 'snapshot') {
156
+ const options = request.params.options || {};
157
+ return applyTranscriptPolicy(kernel.snapshot(checkedTarget(request.params.target), options), options);
158
+ }
69
159
  if (request.method === 'rewind') {
70
160
  return kernel.rewind(checkedTarget(request.params.target), request.params.messageIndex);
71
161
  }
@@ -87,9 +177,6 @@ async function handle(request) {
87
177
  if (request.method === 'context_compress') {
88
178
  return kernel.compressContext(checkedTarget(request.params.target), request.params.options);
89
179
  }
90
- if (request.method === 'rate_auto_route') {
91
- return kernel.rateAutoRoute(checkedTarget(request.params.target), request.params.score, request.params.routeId);
92
- }
93
180
  if (request.method === 'set_work_run_expanded') {
94
181
  return kernel.setWorkRunExpanded(checkedTarget(request.params.target), request.params.runId, request.params.expanded);
95
182
  }
@@ -55,11 +55,27 @@ export interface ModelValidationProgress {
55
55
  check: string;
56
56
  }>;
57
57
  }
58
- export interface AutoRouteRatingResult {
58
+ /** Editable settings for model-backed Auto decisions. */
59
+ export interface JevDecisionModelPatch {
60
+ enabled?: boolean;
61
+ timeoutMs?: number;
62
+ }
63
+ export interface JevDecisionModelState {
64
+ /** `models.auto_switch`: the user's Auto master switch. */
65
+ autoEnabled: boolean;
66
+ /** True when Auto is off: the decision model cannot be edited and cannot move a model. */
67
+ locked: boolean;
68
+ lockReason?: 'auto_disabled';
69
+ enabled: boolean;
70
+ decisionModel: 'current_eligible_model';
71
+ thinkingMode: 'off';
72
+ outputFormat: 'strict_json_schema';
73
+ timeoutMs: number;
74
+ }
75
+ export interface JevDecisionModelUpdateResult {
59
76
  ok: boolean;
60
- score?: -1 | 1;
61
- routeId?: string;
62
- reason?: 'invalid_score' | 'no_active_auto_route' | 'stale_route' | 'already_rated';
77
+ reason?: 'auto_disabled' | 'invalid_value';
78
+ state: JevDecisionModelState;
63
79
  }
64
80
  export declare const ROOT_AGENT_ACTOR_ID = "00000000-0000-4000-8000-000000000001";
65
81
  export declare function normalizeIntelligenceTier(value: unknown): 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'ultra';
@@ -85,6 +101,8 @@ export interface CompressionCacheEntry {
85
101
  foldedChars: number;
86
102
  model: string;
87
103
  fallback: boolean;
104
+ /** Branch node that owns this folded history. Sibling pages must never read it. */
105
+ branchNodeId?: string;
88
106
  }
89
107
  type ConversationModelSelection = {
90
108
  kind: 'auto';
@@ -413,6 +431,8 @@ export declare class Agent {
413
431
  private modelSwitchCompressionPromise;
414
432
  private activePeerAgents;
415
433
  private peerProviderCaches;
434
+ /** Reuses the provider connection for current-model JEV calls without sharing conversation build state. */
435
+ private readonly jevProviderCache;
416
436
  private awaitingAgentKernelRuntime;
417
437
  private pendingAgentKernelQueue;
418
438
  private linkedPlanAccess;
@@ -432,7 +452,7 @@ export declare class Agent {
432
452
  private finalizingWorkRunId;
433
453
  private agentRunService;
434
454
  private agentRunByWorkRunId;
435
- private readonly autoRouter;
455
+ private readonly routeController;
436
456
  private resolvedDeployment;
437
457
  private fixedDeployment;
438
458
  private preferredConversationModelSelection;
@@ -444,11 +464,21 @@ export declare class Agent {
444
464
  private lastRouteRetryDelayMs;
445
465
  private routeAttemptStartedAt;
446
466
  private readonly providerBalanceBlockedUntilByDeployment;
447
- private readonly explicitlyRatedRoutes;
448
467
  private conversationStateCache;
449
468
  private conversationStateCacheFingerprint;
469
+ private conversationStateWrittenFingerprint;
470
+ private conversationShardFingerprint;
471
+ private hydratedShardEntries;
472
+ /**
473
+ * Revision of the durable queue snapshot this runtime is working from. The
474
+ * merge only accepts a writer's queue when that writer has seen the persisted
475
+ * revision, so an outdated in-memory snapshot can neither drop queued work
476
+ * nor resurrect an entry that was already consumed or cleared.
477
+ */
478
+ private continuationRevision;
450
479
  private conversationStateFlushTimers;
451
480
  private conversationStateDirty;
481
+ private conversationStateLastWriteAt;
452
482
  private systemPromptCache;
453
483
  private toolDefinitionCache;
454
484
  private modelValidationPromise;
@@ -528,6 +558,16 @@ export declare class Agent {
528
558
  activeModelName(): string;
529
559
  modelSelectionValue(): string;
530
560
  currentWorkRunId(): string;
561
+ /**
562
+ * The branch node that owns the currently active Build. Provider session ids
563
+ * and branch-scoped caches must use this identity instead of "the branch that
564
+ * happens to be active", otherwise a concurrently running sibling branch
565
+ * (branch-communication mode) or a page switched mid-Build would read another
566
+ * branch's remote session and folded history.
567
+ */
568
+ activeWorkRunBranchId(): string;
569
+ /** Full conversation identity of one branch node (shared by every branch kind). */
570
+ branchConversationIdentity(branchNodeId: string): string;
531
571
  activeModelConfig(): ReturnType<ConfigManager['allModels']>[number] | undefined;
532
572
  requestedModelSelection(): ModelSelection;
533
573
  resetAutoRoute(): void;
@@ -539,12 +579,65 @@ export declare class Agent {
539
579
  recordRouteSuccess(latencyMs?: number, throughput?: number): void;
540
580
  recordRouteToolOutcome(valid: boolean): void;
541
581
  updateProviders(value: unknown): void;
542
- clearLearnedModelPreferences(): void;
543
- rateActiveAutoRoute(score: number, expectedRouteId?: string): AutoRouteRatingResult;
544
- recordObjectiveRouteResult(success: boolean): void;
545
- private recordRouteQualityObservation;
582
+ /**
583
+ * Auto master switch. Every automatic model decision in this runtime is
584
+ * gated on it: when it is off the Agent neither routes nor edits the current
585
+ * model decision settings, and no code path may move the active model on its own.
586
+ */
587
+ autoDecisionEnabled(): boolean;
588
+ /** Current-model JEV settings exposed to GUI / TUI / CLI editors. */
589
+ jevDecisionModelState(): JevDecisionModelState;
590
+ /**
591
+ * Edit current-model Auto decision settings.
592
+ *
593
+ * Refuses every write while Auto is off (`auto_disabled`): with automatic
594
+ * decision-making disabled, not even the decision-model configuration may
595
+ * change — that is what "Auto off never moves a model" means at the editor
596
+ * boundary. Invalid values are rejected without touching the stored config.
597
+ */
598
+ updateJevDecisionModel(patch?: JevDecisionModelPatch): JevDecisionModelUpdateResult;
546
599
  private workspaceConversationKey;
547
600
  private workspaceConversationStorePath;
601
+ /**
602
+ * Heavy conversation payload that lives in a per-conversation shard file
603
+ * instead of the workspace index document. Everything else (title, pinning,
604
+ * order, draft, mode, goal, runtime owner, counters) stays in the index so
605
+ * listing, sorting and cross-process merge never need to parse a transcript.
606
+ */
607
+ private static readonly CONVERSATION_SHARD_FIELDS;
608
+ private workspaceConversationShardDir;
609
+ private conversationShardFileName;
610
+ private conversationShardPath;
611
+ /**
612
+ * Hydrate one conversation entry from its shard on first access. The heavy
613
+ * fields are non-enumerable so `{ ...entry }` (and therefore the index
614
+ * document written back to disk) never pulls a transcript into memory, while
615
+ * every existing reader that asks for `entry.chatMessages` / `entry.history`
616
+ * keeps working unchanged.
617
+ */
618
+ private attachConversationShard;
619
+ /**
620
+ * Heavy payload carried in memory by this entry, or `null` when the entry is
621
+ * a shard-backed index record whose heavy fields were never touched. That
622
+ * distinction is what keeps unrelated conversations off the write path.
623
+ */
624
+ private conversationShardSnapshot;
625
+ /**
626
+ * Force one shard-backed entry into memory. Used only on the rare recovery
627
+ * paths that must compare against a payload written by another runtime.
628
+ */
629
+ private hydrateConversationEntry;
630
+ /**
631
+ * Durable queue merge.
632
+ *
633
+ * Queue entries are the user's pending work, so a runtime that never saw an
634
+ * entry must not delete it, while an entry this runtime actually consumed
635
+ * must not be resurrected. Identity-based union minus exactly the consumed
636
+ * ids gives both guarantees without needing a global revision clock.
637
+ */
638
+ private mergeConversationContinuations;
639
+ /** Index-document form of a conversation entry: metadata plus a shard pointer. */
640
+ private conversationIndexEntryForDisk;
548
641
  private workspaceConversationStateKey;
549
642
  private workspaceConversationStateKeyFor;
550
643
  private workspaceConversationPrefix;
@@ -581,6 +674,16 @@ export declare class Agent {
581
674
  handleImageDisplayWithDescription(args: string, signal?: AbortSignal): Promise<string>;
582
675
  /** Keep media bytes in one content-addressed asset and state.json as refs. */
583
676
  private conversationEntryForDisk;
677
+ /**
678
+ * Bound the live Build ledger.
679
+ *
680
+ * `normalizeWorkRuns()` already windows the persisted/served ledger to 120
681
+ * runs, but the live `this.workRuns` array kept every Build block of the
682
+ * conversation (measured: 307 runs / 4.6 MiB after 300 turns). The renderer
683
+ * only ever shows the newest window, so older terminal runs can be dropped
684
+ * from memory while the conversation continues.
685
+ */
686
+ private pruneLiveWorkRuns;
584
687
  private normalizeWorkRuns;
585
688
  private recoverPersistedWorkRuns;
586
689
  private publishConversationTitle;
@@ -685,7 +788,20 @@ export declare class Agent {
685
788
  private normalizeContinuations;
686
789
  private normalizeConversationPlan;
687
790
  private readStoredConversationState;
791
+ /** Durable replacement-style text write (temp file + fsync + rename). */
792
+ private writeAtomicTextFile;
688
793
  private writeStoredConversationState;
794
+ /**
795
+ * Coalesced conversation-state write.
796
+ *
797
+ * Mid-turn persistence used to rewrite the whole workspace document
798
+ * synchronously on every kernel event (measured: 5.33 full rewrites per turn
799
+ * with no tools). The document grows with the conversation, so that cost grew
800
+ * with the turn count. This path keeps durability at turn boundaries (the
801
+ * terminal save stays synchronous and cancels any pending timer) while
802
+ * throttling intermediate saves: the latest snapshot wins, and bursts inside
803
+ * one throttle window collapse into a single write.
804
+ */
689
805
  private scheduleStoredConversationState;
690
806
  flushWorkspaceConversationState(): void;
691
807
  getStoredFlowSuspension(conversationId?: string, ws?: WorkspaceInfo | null): FlowSuspensionRecord | null;
@@ -1105,10 +1221,20 @@ export declare class Agent {
1105
1221
  private autoSwitchSubset;
1106
1222
  private autoRouteCandidates;
1107
1223
  private persistRouteDecision;
1108
- private loadLearnedRouteFeedback;
1109
- private recordRouteFeedbackFor;
1110
1224
  allModelNames(): string[];
1111
1225
  evaluateAndSwitch(task: string, override?: AgentPromptMessage['routePolicy']): Promise<boolean>;
1226
+ /**
1227
+ * Auto Router v2 decision pipeline:
1228
+ *
1229
+ * RouteEligibilityGuard → JevDecisionEngine → RouteDecisionValidator → pin
1230
+ *
1231
+ * The current conversation model is the only Auto decision source. If it is
1232
+ * disabled, times out, crashes or returns something the guard cannot accept,
1233
+ * Auto stops without selecting another deployment.
1234
+ */
1235
+ private decideAutoRoute;
1236
+ /** Resolve JEV to the current eligible conversation model, never a second model slot. */
1237
+ private resolveCurrentModelJevEngine;
1112
1238
  shouldExposeToolInterface(): boolean;
1113
1239
  modelIsUnavailable(modelName: string): boolean;
1114
1240
  private failedDeploymentsThisRun;
@@ -1229,6 +1355,17 @@ export declare class Agent {
1229
1355
  private formatCompressionSummary;
1230
1356
  private persistCompressedHistory;
1231
1357
  private compressionArchiveScopeKey;
1358
+ /**
1359
+ * The hot folded-history cache belongs to one branch node. Legacy entries
1360
+ * (created before branch scoping) belong to the root node, which is where
1361
+ * they were produced.
1362
+ */
1363
+ private compressionCacheForBranch;
1364
+ /**
1365
+ * Persist the union of every branch's folded history: the live in-memory
1366
+ * cache only holds the active branch, and the stored entry holds the rest.
1367
+ */
1368
+ private mergedCompressionCacheForPersist;
1232
1369
  private coldCompressionEntries;
1233
1370
  private compressionArchiveEntryCount;
1234
1371
  private archiveColdCompressionEntries;