flavor-code 1.3.16 → 1.3.17

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.
@@ -5507,9 +5507,8 @@ var ContextManager = class _ContextManager {
5507
5507
  const currentStableHash = currentStable === void 0 ? this.#epoch.stableSourceHash : hashMessages(currentStable);
5508
5508
  if (currentStable !== void 0 && currentStableHash !== this.#epoch.stableSourceHash) {
5509
5509
  const value = currentStable.map((message2) => message2.content).join("\n\n");
5510
- const previous = this.#epoch.sources["system-baseline"] ?? this.#epoch.stableMessages.map((message2) => message2.content).join("\n\n");
5511
5510
  if (this.#epoch.sources["system-baseline"] !== value) {
5512
- updates.push(contextUpdateMessage("system-baseline", deltaSourceValue(previous, value)));
5511
+ updates.push(contextUpdateMessage("system-baseline", value));
5513
5512
  this.#epoch.sources["system-baseline"] = value;
5514
5513
  }
5515
5514
  } else if (currentStable !== void 0 && this.#epoch.sources["system-baseline"] !== void 0) {
@@ -5530,8 +5529,6 @@ var ContextManager = class _ContextManager {
5530
5529
  }
5531
5530
  if (updates.length === 0) return false;
5532
5531
  this.#messages.push(...updates);
5533
- this.#lastRecordedInputTokens = void 0;
5534
- this.#estimatedTokensAtLastRecordedUsage = void 0;
5535
5532
  return true;
5536
5533
  }
5537
5534
  snapshot() {
@@ -5591,10 +5588,7 @@ var ContextManager = class _ContextManager {
5591
5588
  async prepareForModelCall(signal = new AbortController().signal) {
5592
5589
  signal.throwIfAborted();
5593
5590
  this.refreshContextSources();
5594
- if (this.#dropStaleContextUpdates()) {
5595
- this.#lastRecordedInputTokens = void 0;
5596
- this.#estimatedTokensAtLastRecordedUsage = void 0;
5597
- }
5591
+ this.#dropStaleContextUpdates();
5598
5592
  if (!this.needsCompaction()) return false;
5599
5593
  const originalMessages = this.#messages;
5600
5594
  const originalRecordedUsage = this.#lastRecordedInputTokens;
@@ -5607,8 +5601,6 @@ var ContextManager = class _ContextManager {
5607
5601
  const microcompact = microcompactMessages(this.#messages, this.#compaction.microcompactKeepRecentToolResults);
5608
5602
  if (microcompact.changed) {
5609
5603
  this.#messages = microcompact.messages;
5610
- this.#lastRecordedInputTokens = void 0;
5611
- this.#estimatedTokensAtLastRecordedUsage = void 0;
5612
5604
  }
5613
5605
  if (!this.needsCompaction()) {
5614
5606
  if (microcompact.changed) {
@@ -6572,7 +6564,7 @@ function memoryPressureError(heapUsed, heapLimit) {
6572
6564
  const mb = (bytes) => Math.round(bytes / 1048576);
6573
6565
  return {
6574
6566
  code: "memory_pressure",
6575
- message: `Heap usage reached ${Math.round(heapUsed / heapLimit * 100)}% of the V8 limit (${mb(heapUsed)}MB of ${mb(heapLimit)}MB); stopping to avoid a crash. The session is saved and will resume after restart.`
6567
+ message: `Heap usage reached ${Math.round(heapUsed / heapLimit * 100)}% of the V8 limit (${mb(heapUsed)}MB of ${mb(heapLimit)}MB); stopping the current turn to avoid a crash. The saved session will reopen after restart.`
6576
6568
  };
6577
6569
  }
6578
6570
  function currentMemoryPressure() {
@@ -6692,6 +6684,11 @@ var AgentLoop = class {
6692
6684
  yield { type: "error", error: normalizeProviderError(error) };
6693
6685
  return;
6694
6686
  }
6687
+ const preparedPressure = currentMemoryPressure();
6688
+ if (preparedPressure !== void 0) {
6689
+ yield { type: "error", error: preparedPressure };
6690
+ return;
6691
+ }
6695
6692
  let assistantText = "";
6696
6693
  const collectedToolCalls = [];
6697
6694
  const collectedThinking = [];
@@ -6715,6 +6712,11 @@ var AgentLoop = class {
6715
6712
  tools: [...this.#options.tools],
6716
6713
  ...request.signal === void 0 ? {} : { signal: request.signal }
6717
6714
  };
6715
+ const requestPressure = currentMemoryPressure();
6716
+ if (requestPressure !== void 0) {
6717
+ yield { type: "error", error: requestPressure };
6718
+ return;
6719
+ }
6718
6720
  let before;
6719
6721
  try {
6720
6722
  before = await this.#options.hooks.emit({
@@ -15501,6 +15503,8 @@ function appendThinkingText(previous, delta) {
15501
15503
  }
15502
15504
 
15503
15505
  // src/ui/transcript.ts
15506
+ var MAX_TRANSCRIPT_TURNS = 200;
15507
+ var MAX_TRANSCRIPT_CHARS = 2e6;
15504
15508
  function createTranscriptState() {
15505
15509
  return { completed: [], nextId: 1 };
15506
15510
  }
@@ -15797,7 +15801,7 @@ function hydrateHistory(messages, compact) {
15797
15801
  }
15798
15802
  }
15799
15803
  if (turn !== void 0) completed.push(turn);
15800
- return { completed, nextId: completed.length + 1 };
15804
+ return { completed: boundedCompleted(completed), nextId: completed.length + 1 };
15801
15805
  }
15802
15806
  function transcriptFromLegacyConversation(messages, compact) {
15803
15807
  return hydrateHistory(messages, compact);
@@ -15891,10 +15895,10 @@ function finishActive(state) {
15891
15895
  if (state.active === void 0) return state;
15892
15896
  const active = withoutModelActivity(state.active);
15893
15897
  return {
15894
- completed: [...state.completed, {
15898
+ completed: boundedCompleted([...state.completed, {
15895
15899
  ...active,
15896
15900
  completedAt: active.completedAt ?? (/* @__PURE__ */ new Date()).toISOString()
15897
- }],
15901
+ }]),
15898
15902
  nextId: state.nextId,
15899
15903
  ...state.taskSnapshot === void 0 ? {} : { taskSnapshot: state.taskSnapshot }
15900
15904
  };
@@ -15923,12 +15927,26 @@ function withoutModelActivity(turn, id) {
15923
15927
  function restoreTranscriptState(state) {
15924
15928
  const completed = state.completed.map(cloneTurn);
15925
15929
  if (state.active !== void 0) completed.push(normalizeInterruptedTurn(cloneTurn(state.active)));
15930
+ const nextId = Math.max(state.nextId, ...completed.map((turn) => turn.id + 1), 1);
15926
15931
  return {
15927
- completed,
15928
- nextId: Math.max(state.nextId, ...completed.map((turn) => turn.id + 1), 1),
15932
+ completed: boundedCompleted(completed),
15933
+ nextId,
15929
15934
  ...state.taskSnapshot === void 0 ? {} : { taskSnapshot: state.taskSnapshot }
15930
15935
  };
15931
15936
  }
15937
+ function boundedCompleted(input) {
15938
+ const recent = input.slice(-MAX_TRANSCRIPT_TURNS);
15939
+ let chars = 0;
15940
+ let start = recent.length;
15941
+ for (let index = recent.length - 1; index >= 0; index -= 1) {
15942
+ const turn = recent[index];
15943
+ const size = JSON.stringify(turn).length;
15944
+ if (start < recent.length && chars + size > MAX_TRANSCRIPT_CHARS) break;
15945
+ chars += size;
15946
+ start = index;
15947
+ }
15948
+ return recent.slice(start);
15949
+ }
15932
15950
  function compactionTurn(compact) {
15933
15951
  const text = `Original execution steps before ${compact.compactedAt} are unavailable.`;
15934
15952
  return {
@@ -16752,6 +16770,8 @@ function isCode9(error, code) {
16752
16770
  }
16753
16771
 
16754
16772
  // src/session/tree.ts
16773
+ var MAX_SESSION_HISTORY_NODES = 100;
16774
+ var MAX_SESSION_HISTORY_CONTEXT_CHARS = 2e6;
16755
16775
  var SessionHistory = class _SessionHistory {
16756
16776
  #path;
16757
16777
  #checkpoints;
@@ -16763,8 +16783,8 @@ var SessionHistory = class _SessionHistory {
16763
16783
  this.#path = join25(resolve21(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
16764
16784
  this.#checkpoints = new WorkspaceCheckpointStore({ workspace: options.workspace });
16765
16785
  this.#restoreContext = options.restoreContext;
16766
- this.#nodes = document.nodes;
16767
- this.#leafId = document.leafId;
16786
+ this.#nodes = boundedNodes(document.nodes);
16787
+ this.#leafId = document.leafId !== null && this.#nodes.some((node) => node.id === document.leafId) ? document.leafId : this.#nodes.at(-1)?.id ?? null;
16768
16788
  }
16769
16789
  static async open(options) {
16770
16790
  const path = join25(resolve21(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
@@ -16776,7 +16796,9 @@ var SessionHistory = class _SessionHistory {
16776
16796
  } catch (error) {
16777
16797
  if (!isCode10(error, "ENOENT")) throw error;
16778
16798
  }
16779
- return new _SessionHistory(options, document);
16799
+ const history = new _SessionHistory(options, document);
16800
+ if (history.#nodes.length !== document.nodes.length) await history.#persist();
16801
+ return history;
16780
16802
  }
16781
16803
  get leafId() {
16782
16804
  return this.#leafId;
@@ -16796,6 +16818,7 @@ var SessionHistory = class _SessionHistory {
16796
16818
  };
16797
16819
  this.#nodes.push(node);
16798
16820
  this.#leafId = node.id;
16821
+ this.#boundNodes();
16799
16822
  await this.#persist();
16800
16823
  return structuredClone(node);
16801
16824
  }
@@ -16837,6 +16860,13 @@ var SessionHistory = class _SessionHistory {
16837
16860
  if (node === void 0) throw new Error(`Unknown session tree node: ${id}`);
16838
16861
  return node;
16839
16862
  }
16863
+ #boundNodes() {
16864
+ const bounded2 = boundedNodes(this.#nodes);
16865
+ this.#nodes.splice(0, this.#nodes.length, ...bounded2);
16866
+ if (this.#leafId !== null && !this.#nodes.some((node) => node.id === this.#leafId)) {
16867
+ this.#leafId = this.#nodes.at(-1)?.id ?? null;
16868
+ }
16869
+ }
16840
16870
  async #persist() {
16841
16871
  const document = {
16842
16872
  version: 1,
@@ -16851,6 +16881,24 @@ var SessionHistory = class _SessionHistory {
16851
16881
  await rename8(temporary, this.#path);
16852
16882
  }
16853
16883
  };
16884
+ function boundedNodes(input) {
16885
+ const recent = input.slice(-MAX_SESSION_HISTORY_NODES);
16886
+ let contextChars = 0;
16887
+ let start = recent.length;
16888
+ for (let index = recent.length - 1; index >= 0; index -= 1) {
16889
+ const node = recent[index];
16890
+ const size = JSON.stringify(node.context).length;
16891
+ if (start < recent.length && contextChars + size > MAX_SESSION_HISTORY_CONTEXT_CHARS) break;
16892
+ contextChars += size;
16893
+ start = index;
16894
+ }
16895
+ const retained = recent.slice(start).map((node) => structuredClone(node));
16896
+ const ids = new Set(retained.map((node) => node.id));
16897
+ for (const node of retained) {
16898
+ if (node.parentId !== null && !ids.has(node.parentId)) node.parentId = null;
16899
+ }
16900
+ return retained;
16901
+ }
16854
16902
  function validate(value, sessionId) {
16855
16903
  if (value.version !== 1 || value.sessionId !== sessionId || !Array.isArray(value.nodes)) {
16856
16904
  throw new Error("Invalid session tree");
@@ -26325,7 +26373,7 @@ async function createProductionRuntime(options) {
26325
26373
  contextEpoch: harness.main.context.snapshot().epoch?.id ?? "legacy"
26326
26374
  });
26327
26375
  const turnId = harnessJournal.startTurn(turnConfig, { prompt, initialUserMessage: runOptions?.initialUserMessage });
26328
- return monitorMemoryRestart(durableTurn(persistAndCheckpointAfter(runMain(
26376
+ return monitorMemoryRestart(durableTurn(persistAfter(runMain(
26329
26377
  harness,
26330
26378
  skills,
26331
26379
  prompt,
@@ -26341,11 +26389,7 @@ async function createProductionRuntime(options) {
26341
26389
  runOptions?.initialUserMessage,
26342
26390
  runOptions?.additionalContext,
26343
26391
  ide
26344
- ), persist, () => sessionHistory.append({
26345
- prompt,
26346
- context: harness.main.context.snapshot(),
26347
- label: `turn: ${prompt.slice(0, 80)}`
26348
- })), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
26392
+ ), persist), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
26349
26393
  },
26350
26394
  runSkill: (skill, prompt, signal) => persistAfter(
26351
26395
  runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
@@ -27055,14 +27099,6 @@ async function* persistAfter(source, persist) {
27055
27099
  await persist();
27056
27100
  }
27057
27101
  }
27058
- async function* persistAndCheckpointAfter(source, persist, checkpoint) {
27059
- try {
27060
- for await (const item of source) yield item;
27061
- await checkpoint();
27062
- } finally {
27063
- await persist();
27064
- }
27065
- }
27066
27102
  async function* persistEach(source, persist) {
27067
27103
  try {
27068
27104
  for await (const item of source) {