flavor-code 1.3.15 → 1.3.16

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.
@@ -4,7 +4,16 @@ import type { HookBus } from "../hooks/bus.js";
4
4
  import type { ModelRegistry } from "../models/registry.js";
5
5
  import { type ModelMessage, type ModelTool } from "../models/types.js";
6
6
  import type { ToolRuntime } from "../tools/runtime.js";
7
- import type { AgentEvent, AgentRunRequest } from "./types.js";
7
+ import type { AgentError, AgentEvent, AgentRunRequest } from "./types.js";
8
+ /**
9
+ * Stop a turn before the V8 heap reaches its hard limit. A retained-memory
10
+ * leak otherwise ends in a native OOM crash with no recovery; tripping this
11
+ * watermark saves the session and lets the launcher relaunch on a fresh heap.
12
+ * 0.8 keeps headroom for compaction payloads and the GC death spiral that
13
+ * starts near ~0.95.
14
+ */
15
+ export declare const MEMORY_PRESSURE_HEAP_RATIO = 0.8;
16
+ export declare function memoryPressureError(heapUsed: number, heapLimit: number): AgentError | undefined;
8
17
  export interface AgentLoopOptions {
9
18
  registry: ModelRegistry;
10
19
  modelId: string;
@@ -15,7 +15,7 @@ export interface AgentRunRequest {
15
15
  /** Messages supplied while a tool batch is running, injected before the next model call. */
16
16
  getSteeringMessages?(): readonly string[];
17
17
  }
18
- export type AgentErrorCode = ProviderErrorCode | "iteration_limit" | "incomplete_stream" | "empty_response";
18
+ export type AgentErrorCode = ProviderErrorCode | "iteration_limit" | "incomplete_stream" | "empty_response" | "memory_pressure";
19
19
  export interface AgentError {
20
20
  code: AgentErrorCode;
21
21
  message: string;
@@ -18,8 +18,9 @@ import {
18
18
  thinkingWindow,
19
19
  transcriptReducer,
20
20
  withTimeout
21
- } from "./chunk-WB4IJXFT.js";
22
- import "./chunk-JFKRX2KG.js";
21
+ } from "./chunk-S525EOFQ.js";
22
+ import "./chunk-CIVPKHF4.js";
23
+ import "./chunk-HJOCNKPN.js";
23
24
  import {
24
25
  Box_default,
25
26
  ClockContext,
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
3
+
4
+ // src/utils/memory-restart.ts
5
+ import { join } from "path";
6
+ var MEMORY_RESTART_EXIT_CODE = 75;
7
+ var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
8
+ var MEMORY_RESTART_MAX_ATTEMPTS = 3;
9
+ var MEMORY_RESTART_WINDOW_MS = 30 * 60 * 1e3;
10
+ var SESSION_ID_PATTERN = /^session-[A-Za-z0-9_-]+$/u;
11
+ function memoryRestartMarkerPath(workspace) {
12
+ return join(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
13
+ }
14
+ function nextMemoryRestartMarker(previous, sessionId, now) {
15
+ const reusable = previous !== void 0 && previous.sessionId === sessionId && now.getTime() - Date.parse(previous.requestedAt) < MEMORY_RESTART_WINDOW_MS && Number.isInteger(previous.attempts) && previous.attempts > 0;
16
+ return { sessionId, requestedAt: now.toISOString(), attempts: reusable ? previous.attempts + 1 : 1 };
17
+ }
18
+ function shouldRelaunchForMemoryRestart(marker) {
19
+ return marker !== void 0 && SESSION_ID_PATTERN.test(marker.sessionId) && Number.isInteger(marker.attempts) && marker.attempts >= 1 && marker.attempts <= MEMORY_RESTART_MAX_ATTEMPTS;
20
+ }
21
+ function memoryRestartArgs(argv, marker) {
22
+ if (!shouldRelaunchForMemoryRestart(marker)) return void 0;
23
+ const stripped = [];
24
+ for (let index = 0; index < argv.length; index += 1) {
25
+ const argument = argv[index];
26
+ if (argument === "--resume") {
27
+ if (index + 1 < argv.length && !argv[index + 1].startsWith("-")) index += 1;
28
+ continue;
29
+ }
30
+ if (argument.startsWith("--resume=")) continue;
31
+ stripped.push(argument);
32
+ }
33
+ return [...stripped, "--resume", marker.sessionId];
34
+ }
35
+ function parseMemoryRestartMarker(json) {
36
+ try {
37
+ const value = JSON.parse(json);
38
+ if (typeof value.sessionId !== "string" || typeof value.requestedAt !== "string" || typeof value.attempts !== "number") {
39
+ return void 0;
40
+ }
41
+ return { sessionId: value.sessionId, requestedAt: value.requestedAt, attempts: value.attempts };
42
+ } catch {
43
+ return void 0;
44
+ }
45
+ }
46
+
47
+ export {
48
+ MEMORY_RESTART_EXIT_CODE,
49
+ memoryRestartMarkerPath,
50
+ nextMemoryRestartMarker,
51
+ memoryRestartArgs,
52
+ parseMemoryRestartMarker
53
+ };
@@ -129,7 +129,7 @@ var FlavorConfigSchema = z.object({
129
129
  context: z.object({
130
130
  windowTokens: z.number().int().positive().default(2e5),
131
131
  reservedOutputTokens: z.number().int().nonnegative().default(2e4),
132
- autoCompactBufferTokens: z.number().int().nonnegative().default(13e3),
132
+ autoCompactBufferTokens: z.number().int().nonnegative().default(27e3),
133
133
  warningBufferTokens: z.number().int().nonnegative().default(2e4),
134
134
  blockingBufferTokens: z.number().int().nonnegative().default(3e3),
135
135
  microcompactKeepRecentToolResults: z.number().int().nonnegative().default(5),
@@ -4,7 +4,7 @@ import {
4
4
  message,
5
5
  redactErrorText,
6
6
  redactSecrets
7
- } from "./chunk-WB4IJXFT.js";
7
+ } from "./chunk-S525EOFQ.js";
8
8
 
9
9
  // src/rpc/server.ts
10
10
  import { createInterface } from "readline";
@@ -1,5 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
3
+ import {
4
+ MEMORY_RESTART_EXIT_CODE,
5
+ memoryRestartMarkerPath,
6
+ nextMemoryRestartMarker,
7
+ parseMemoryRestartMarker
8
+ } from "./chunk-CIVPKHF4.js";
3
9
  import {
4
10
  PERMISSION_MODES,
5
11
  PermissionModeSchema,
@@ -12,7 +18,7 @@ import {
12
18
  redactConfig,
13
19
  setProjectMcpServerDisabled,
14
20
  updateProtectedFile
15
- } from "./chunk-JFKRX2KG.js";
21
+ } from "./chunk-HJOCNKPN.js";
16
22
  import {
17
23
  AuditLogger,
18
24
  appendUsageLog,
@@ -5326,9 +5332,10 @@ async function ensureOnce(options) {
5326
5332
 
5327
5333
  // src/production.ts
5328
5334
  import { readFile as readFile22 } from "fs/promises";
5335
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync } from "fs";
5329
5336
  import { createHash as createHash18, randomUUID as randomUUID20 } from "crypto";
5330
5337
  import { homedir as homedir2, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
5331
- import { basename as basename6, join as join26, relative as relative20, resolve as resolve28, sep as sep20 } from "path";
5338
+ import { basename as basename6, dirname as dirname19, join as join26, relative as relative20, resolve as resolve28, sep as sep20 } from "path";
5332
5339
 
5333
5340
  // src/agent/subagents.ts
5334
5341
  import { z as z5 } from "zod";
@@ -5877,7 +5884,9 @@ import { createHash as createHash4, randomUUID as randomUUID6 } from "crypto";
5877
5884
  var DEFAULT_COMPACTION_POLICY = {
5878
5885
  windowTokens: 2e5,
5879
5886
  reservedOutputTokens: 2e4,
5880
- autoCompactBufferTokens: 13e3,
5887
+ // 85% of the effective window (180_000 - 27_000 = 153_000): compaction used
5888
+ // to trigger at ~92.8%, which let sessions ride the threshold into heap OOM.
5889
+ autoCompactBufferTokens: 27e3,
5881
5890
  warningBufferTokens: 2e4,
5882
5891
  blockingBufferTokens: 3e3,
5883
5892
  microcompactKeepRecentToolResults: 5,
@@ -6087,6 +6096,13 @@ var ContextManager = class _ContextManager {
6087
6096
  #epoch;
6088
6097
  #visibilityLog = [];
6089
6098
  #activeTransientSystem = /* @__PURE__ */ new Map();
6099
+ /**
6100
+ * The visibility audit log persists every transient system admission and is
6101
+ * serialized in full on every session save. Bound both dimensions or a long
6102
+ * subagent-driven run grows it into hundreds of megabytes (heap OOM).
6103
+ */
6104
+ static VISIBILITY_LOG_MAX_ENTRIES = 1e3;
6105
+ static VISIBILITY_RECORD_CONTENT_LIMIT = 2048;
6090
6106
  constructor(options) {
6091
6107
  if (options.compactAtChars !== void 0 && options.compactAtChars <= 0) throw new Error("compactAtChars must be positive");
6092
6108
  if (options.toolOutputChars <= 0) throw new Error("toolOutputChars must be positive");
@@ -6182,11 +6198,14 @@ var ContextManager = class _ContextManager {
6182
6198
  const record = {
6183
6199
  id,
6184
6200
  role: "system",
6185
- content,
6201
+ content: limitVisibilityContent(content),
6186
6202
  admittedAt: (/* @__PURE__ */ new Date()).toISOString(),
6187
6203
  scope: "run"
6188
6204
  };
6189
6205
  this.#visibilityLog.push(record);
6206
+ if (this.#visibilityLog.length > _ContextManager.VISIBILITY_LOG_MAX_ENTRIES) {
6207
+ this.#visibilityLog = this.#visibilityLog.slice(-_ContextManager.VISIBILITY_LOG_MAX_ENTRIES);
6208
+ }
6190
6209
  this.#activeTransientSystem.set(id, { role: "system", content });
6191
6210
  return id;
6192
6211
  }
@@ -6210,8 +6229,9 @@ var ContextManager = class _ContextManager {
6210
6229
  const currentStableHash = currentStable === void 0 ? this.#epoch.stableSourceHash : hashMessages(currentStable);
6211
6230
  if (currentStable !== void 0 && currentStableHash !== this.#epoch.stableSourceHash) {
6212
6231
  const value = currentStable.map((message2) => message2.content).join("\n\n");
6232
+ const previous = this.#epoch.sources["system-baseline"] ?? this.#epoch.stableMessages.map((message2) => message2.content).join("\n\n");
6213
6233
  if (this.#epoch.sources["system-baseline"] !== value) {
6214
- updates.push(contextUpdateMessage("system-baseline", value));
6234
+ updates.push(contextUpdateMessage("system-baseline", deltaSourceValue(previous, value)));
6215
6235
  this.#epoch.sources["system-baseline"] = value;
6216
6236
  }
6217
6237
  } else if (currentStable !== void 0 && this.#epoch.sources["system-baseline"] !== void 0) {
@@ -6222,7 +6242,7 @@ var ContextManager = class _ContextManager {
6222
6242
  for (const key of DYNAMIC_SOURCE_ORDER) {
6223
6243
  const value = next[key];
6224
6244
  if (value === void 0 || value === this.#epoch.sources[key]) continue;
6225
- updates.push(contextUpdateMessage(key, displaySourceValue(key, value)));
6245
+ updates.push(contextUpdateMessage(key, deltaSourceValue(this.#epoch.sources[key], displaySourceValue(key, value))));
6226
6246
  this.#epoch.sources[key] = value;
6227
6247
  }
6228
6248
  for (const key of DYNAMIC_SOURCE_ORDER) {
@@ -6249,7 +6269,7 @@ var ContextManager = class _ContextManager {
6249
6269
  const legacySummary = snapshot4.summary?.role === "system" && snapshot4.summary.content.startsWith("Conversation summary\n") ? snapshot4.summary.content.slice("Conversation summary\n".length) : void 0;
6250
6270
  this.#compact = snapshot4.compact === void 0 ? legacySummary === void 0 ? void 0 : { summary: legacySummary, compactedAt: (/* @__PURE__ */ new Date(0)).toISOString() } : { ...snapshot4.compact };
6251
6271
  if (snapshot4.epoch !== void 0) this.#epoch = cloneEpoch(snapshot4.epoch);
6252
- this.#visibilityLog = snapshot4.visibilityLog?.map((item) => ({ ...item })) ?? [];
6272
+ this.#visibilityLog = boundVisibilityLog(snapshot4.visibilityLog);
6253
6273
  this.#activeTransientSystem.clear();
6254
6274
  this.#messages = messages.map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
6255
6275
  this.#lastRecordedInputTokens = void 0;
@@ -6293,6 +6313,10 @@ var ContextManager = class _ContextManager {
6293
6313
  async prepareForModelCall(signal = new AbortController().signal) {
6294
6314
  signal.throwIfAborted();
6295
6315
  this.refreshContextSources();
6316
+ if (this.#dropStaleContextUpdates()) {
6317
+ this.#lastRecordedInputTokens = void 0;
6318
+ this.#estimatedTokensAtLastRecordedUsage = void 0;
6319
+ }
6296
6320
  if (!this.needsCompaction()) return false;
6297
6321
  const originalMessages = this.#messages;
6298
6322
  const originalRecordedUsage = this.#lastRecordedInputTokens;
@@ -6392,6 +6416,26 @@ var ContextManager = class _ContextManager {
6392
6416
  this.#reportCompactProgress(100);
6393
6417
  return true;
6394
6418
  }
6419
+ #dropStaleContextUpdates() {
6420
+ const latestBySource = /* @__PURE__ */ new Map();
6421
+ for (let index = this.#messages.length - 1; index >= 0; index -= 1) {
6422
+ const source = contextUpdateSource(this.#messages[index]);
6423
+ if (source === void 0 || latestBySource.has(source)) continue;
6424
+ latestBySource.set(source, index);
6425
+ }
6426
+ const next = [];
6427
+ let changed = false;
6428
+ this.#messages.forEach((message2, index) => {
6429
+ const source = contextUpdateSource(message2);
6430
+ if (source !== void 0 && latestBySource.get(source) !== index) {
6431
+ changed = true;
6432
+ return;
6433
+ }
6434
+ next.push(message2);
6435
+ });
6436
+ if (changed) this.#messages = next;
6437
+ return changed;
6438
+ }
6395
6439
  #reportCompactProgress(percentage) {
6396
6440
  const normalized = Math.max(0, Math.min(100, Math.floor(percentage / 10) * 10));
6397
6441
  if (normalized === this.#lastCompactProgress) return;
@@ -6503,6 +6547,33 @@ function contextUpdateMessage(source, content) {
6503
6547
  return { role: "system", content: `Context update [${source}]
6504
6548
  ${content}` };
6505
6549
  }
6550
+ var CONTEXT_UPDATE_PREFIX = "Context update [";
6551
+ function contextUpdateSource(message2) {
6552
+ if (message2.role !== "system" || typeof message2.content !== "string") return void 0;
6553
+ if (!message2.content.startsWith(CONTEXT_UPDATE_PREFIX)) return void 0;
6554
+ const end = message2.content.indexOf("]\n", CONTEXT_UPDATE_PREFIX.length);
6555
+ if (end < 0) return void 0;
6556
+ return message2.content.slice(CONTEXT_UPDATE_PREFIX.length, end);
6557
+ }
6558
+ function deltaSourceValue(previous, display) {
6559
+ if (previous === void 0 || !display.startsWith(previous)) return display;
6560
+ return display.slice(previous.length).replace(/^\s+/, "") || display;
6561
+ }
6562
+ function limitVisibilityContent(content) {
6563
+ const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
6564
+ if (content.length <= limit) return content;
6565
+ return `${content.slice(0, limit)}
6566
+ ...[truncated; original length: ${content.length} characters]...`;
6567
+ }
6568
+ function boundVisibilityLog(records) {
6569
+ if (records === void 0) return [];
6570
+ const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
6571
+ const bounded2 = records.map((item) => ({
6572
+ ...item,
6573
+ ...item.content.length <= limit ? {} : { content: limitVisibilityContent(item.content) }
6574
+ }));
6575
+ return bounded2.length > ContextManager.VISIBILITY_LOG_MAX_ENTRIES ? bounded2.slice(-ContextManager.VISIBILITY_LOG_MAX_ENTRIES) : bounded2;
6576
+ }
6506
6577
  function cloneEpoch(epoch) {
6507
6578
  return {
6508
6579
  ...epoch,
@@ -6797,6 +6868,9 @@ function reportProgress(callback, percentage) {
6797
6868
  }
6798
6869
  }
6799
6870
 
6871
+ // src/agent/loop.ts
6872
+ import { getHeapStatistics } from "v8";
6873
+
6800
6874
  // src/models/structured.ts
6801
6875
  import { z as z7 } from "zod";
6802
6876
  var DEFAULT_RETRY_DELAYS = [1e3, 2e3, 4e3];
@@ -7214,6 +7288,18 @@ function envMaxIterations() {
7214
7288
  if (!Number.isInteger(parsed) || parsed <= 0) return void 0;
7215
7289
  return parsed;
7216
7290
  }
7291
+ var MEMORY_PRESSURE_HEAP_RATIO = 0.8;
7292
+ function memoryPressureError(heapUsed, heapLimit) {
7293
+ if (heapLimit <= 0 || heapUsed < heapLimit * MEMORY_PRESSURE_HEAP_RATIO) return void 0;
7294
+ const mb = (bytes) => Math.round(bytes / 1048576);
7295
+ return {
7296
+ code: "memory_pressure",
7297
+ 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.`
7298
+ };
7299
+ }
7300
+ function currentMemoryPressure() {
7301
+ return memoryPressureError(process.memoryUsage().heapUsed, getHeapStatistics().heap_size_limit);
7302
+ }
7217
7303
  var AgentLoop = class {
7218
7304
  #options;
7219
7305
  #iterationLimitMode = "standard";
@@ -7284,6 +7370,11 @@ var AgentLoop = class {
7284
7370
  yield { type: "error", error: { code: "cancelled", message: abortMessage(request.signal) } };
7285
7371
  return;
7286
7372
  }
7373
+ const pressure = currentMemoryPressure();
7374
+ if (pressure !== void 0) {
7375
+ yield { type: "error", error: pressure };
7376
+ return;
7377
+ }
7287
7378
  const softLimit = Math.floor(maxIterations * this.#options.softLimitFactor);
7288
7379
  if (!warned && iteration >= softLimit) {
7289
7380
  warned = true;
@@ -12679,7 +12770,8 @@ var BoundedOutput = class {
12679
12770
  #headLimit;
12680
12771
  #tailLimit;
12681
12772
  #head = Buffer.alloc(0);
12682
- #tail = Buffer.alloc(0);
12773
+ #tailChunks = [];
12774
+ #tailBytes = 0;
12683
12775
  #complete = Buffer.alloc(0);
12684
12776
  #bytes = 0;
12685
12777
  constructor(limit) {
@@ -12699,15 +12791,24 @@ var BoundedOutput = class {
12699
12791
  const count = Math.min(this.#headLimit - this.#head.length, chunk.length);
12700
12792
  this.#head = Buffer.concat([this.#head, chunk.subarray(0, count)]);
12701
12793
  }
12702
- if (this.#tailLimit > 0) this.#tail = Buffer.concat([this.#tail, chunk]).subarray(-this.#tailLimit);
12794
+ if (this.#tailLimit > 0) {
12795
+ this.#tailChunks.push(chunk);
12796
+ this.#tailBytes += chunk.length;
12797
+ if (this.#tailBytes >= this.#tailLimit * 2) {
12798
+ const merged = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
12799
+ this.#tailChunks = [merged];
12800
+ this.#tailBytes = merged.length;
12801
+ }
12802
+ }
12703
12803
  }
12704
12804
  metadata() {
12705
12805
  return { truncated: this.truncated, originalBytes: this.#bytes, limitBytes: this.#limit };
12706
12806
  }
12707
12807
  text() {
12708
12808
  if (!this.truncated) return decodeOutput(this.#complete);
12809
+ const tail = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
12709
12810
  const encoding = classifyUtf8(this.#complete) === "invalid" && process.platform === "win32" ? "gb18030" : "utf-8";
12710
- return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(this.#tail, encoding)}`;
12811
+ return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(tail, encoding)}`;
12711
12812
  }
12712
12813
  };
12713
12814
  var AdaptiveOutputDecoder = class {
@@ -22634,7 +22735,7 @@ async function createProductionRuntime(options) {
22634
22735
  contextEpoch: harness.main.context.snapshot().epoch?.id ?? "legacy"
22635
22736
  });
22636
22737
  const turnId = harnessJournal.startTurn(turnConfig, { prompt, initialUserMessage: runOptions?.initialUserMessage });
22637
- return durableTurn(persistAndCheckpointAfter(runMain(
22738
+ return monitorMemoryRestart(durableTurn(persistAndCheckpointAfter(runMain(
22638
22739
  harness,
22639
22740
  skills,
22640
22741
  prompt,
@@ -22654,7 +22755,7 @@ async function createProductionRuntime(options) {
22654
22755
  prompt,
22655
22756
  context: harness.main.context.snapshot(),
22656
22757
  label: `turn: ${prompt.slice(0, 80)}`
22657
- })), harnessJournal, turnId, turnConfig);
22758
+ })), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
22658
22759
  },
22659
22760
  runSkill: (skill, prompt, signal) => persistAfter(
22660
22761
  runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
@@ -23403,6 +23504,31 @@ async function* durableTurn(source, journal, turnId, config) {
23403
23504
  }
23404
23505
  }
23405
23506
  }
23507
+ async function* monitorMemoryRestart(source, onRestartNeeded) {
23508
+ let pressure = false;
23509
+ for await (const event of source) {
23510
+ if (event.type === "error" && event.error.code === "memory_pressure") pressure = true;
23511
+ yield event;
23512
+ }
23513
+ if (pressure) onRestartNeeded();
23514
+ }
23515
+ function requestMemoryRestart(workspace, sessionId, emitOutput) {
23516
+ try {
23517
+ const markerPath = memoryRestartMarkerPath(workspace);
23518
+ let previous;
23519
+ try {
23520
+ previous = parseMemoryRestartMarker(readFileSync3(markerPath, "utf8"));
23521
+ } catch {
23522
+ }
23523
+ mkdirSync3(dirname19(markerPath), { recursive: true });
23524
+ writeFileSync(markerPath, `${JSON.stringify(nextMemoryRestartMarker(previous, sessionId, /* @__PURE__ */ new Date()))}
23525
+ `, "utf8");
23526
+ } catch {
23527
+ }
23528
+ process.exitCode = MEMORY_RESTART_EXIT_CODE;
23529
+ emitOutput({ type: "notice", message: "Memory watermark reached; the session is saved and Flavor is restarting with a fresh heap." });
23530
+ emitOutput({ type: "exit" });
23531
+ }
23406
23532
  async function runGitCommit(deps, hint, signal) {
23407
23533
  if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
23408
23534
  let summary2 = await changeSummary(deps.workspace);
package/dist/cli-main.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module"; const require = createRequire(impor
3
3
  import {
4
4
  FlavorRpcServer,
5
5
  TraceRecorder
6
- } from "./chunk-TQXNF337.js";
6
+ } from "./chunk-LMFN2QEA.js";
7
7
  import {
8
8
  staticTaskLines
9
9
  } from "./chunk-2BF7CVWZ.js";
@@ -23,7 +23,8 @@ import {
23
23
  packageVersion,
24
24
  redactErrorText,
25
25
  runPalBroker
26
- } from "./chunk-WB4IJXFT.js";
26
+ } from "./chunk-S525EOFQ.js";
27
+ import "./chunk-CIVPKHF4.js";
27
28
  import {
28
29
  McpServerConfigSchema,
29
30
  McpServerNameSchema,
@@ -32,7 +33,7 @@ import {
32
33
  readRecoverableFile,
33
34
  setProjectSkillDisabled,
34
35
  updateProtectedFile
35
- } from "./chunk-JFKRX2KG.js";
36
+ } from "./chunk-HJOCNKPN.js";
36
37
  import "./chunk-RQTYHUMK.js";
37
38
  import "./chunk-S32JRDZK.js";
38
39
 
@@ -750,7 +751,7 @@ async function runEvaluationFile(path, outputPath) {
750
751
  })),
751
752
  ...parsed.maxTokens === void 0 ? {} : { maxTokens: parsed.maxTokens }
752
753
  };
753
- const configured = await import("./load-LTTKVPJZ.js").then(({ loadConfig: loadConfig2 }) => loadConfig2({ cwd: workspace, home: process.env.USERPROFILE ?? process.env.HOME ?? workspace }));
754
+ const configured = await import("./load-DELQW42K.js").then(({ loadConfig: loadConfig2 }) => loadConfig2({ cwd: workspace, home: process.env.USERPROFILE ?? process.env.HOME ?? workspace }));
754
755
  const environment = createExecutionEnvironment(workspace, configured.config.execution) ?? new LocalExecutionEnvironment();
755
756
  let report;
756
757
  try {
@@ -877,7 +878,7 @@ async function runInteractiveCli(props) {
877
878
  const [{ render, AlternateScreen }, { createElement }, { App }] = await Promise.all([
878
879
  import("./claude-ink-ORUA7GWG.js"),
879
880
  import("react"),
880
- import("./app-DDYOKGCX.js")
881
+ import("./app-6SEGTMMC.js")
881
882
  ]);
882
883
  const instance = await render(createElement(
883
884
  AlternateScreen,
package/dist/cli.js CHANGED
@@ -1,10 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
3
+ import {
4
+ MEMORY_RESTART_EXIT_CODE,
5
+ memoryRestartArgs,
6
+ memoryRestartMarkerPath,
7
+ parseMemoryRestartMarker
8
+ } from "./chunk-CIVPKHF4.js";
3
9
  import "./chunk-S32JRDZK.js";
4
10
 
5
11
  // src/launcher.ts
6
12
  import { spawn } from "child_process";
7
- import { realpathSync } from "fs";
13
+ import { realpathSync, readFileSync } from "fs";
8
14
  import { release } from "os";
9
15
  import { fileURLToPath } from "url";
10
16
  function needsWindowsMaglevWorkaround(runtime) {
@@ -14,9 +20,23 @@ function needsWindowsMaglevWorkaround(runtime) {
14
20
  if (build !== 26200 || major < 24) return false;
15
21
  return !runtime.execArgv.some((argument) => argument === "--no-maglev" || argument === "--jitless");
16
22
  }
23
+ function needsRelaunch(runtime) {
24
+ if (needsWindowsMaglevWorkaround(runtime)) return true;
25
+ if (!runtime.execArgv.some((argument) => argument === "--report-on-fatalerror")) return true;
26
+ return !runtime.execArgv.some((argument) => argument.startsWith("--heapsnapshot-near-heap-limit"));
27
+ }
17
28
  function cliMainArguments(runtime, mainPath, argv) {
18
29
  return [
19
30
  ...needsWindowsMaglevWorkaround(runtime) ? ["--no-maglev"] : [],
31
+ // Fatal V8 errors (heap OOM, native crashes) bypass crash-guard.ts and
32
+ // leave only an unusable native stack. Ask Node to dump a diagnostic
33
+ // report next to the working directory so the next crash has a scene.
34
+ "--report-on-fatalerror",
35
+ // The 2026-08-29 OOM report showed 4.29GB retained with only ~130K
36
+ // context tokens, and no static candidate explained the retention.
37
+ // Capture one heap snapshot as the heap nears its limit so the next
38
+ // crash leaves a .heapsnapshot retainer trail next to the report.
39
+ "--heapsnapshot-near-heap-limit=1",
20
40
  mainPath,
21
41
  ...argv
22
42
  ];
@@ -29,26 +49,48 @@ async function launchCli() {
29
49
  nodeVersion: process.version,
30
50
  execArgv: process.execArgv
31
51
  };
32
- if (!needsWindowsMaglevWorkaround(runtime)) {
52
+ if (!needsRelaunch(runtime)) {
33
53
  const cli = await import(mainUrl.href);
34
54
  await cli.runCli(process.argv);
35
55
  return;
36
56
  }
37
- const child = spawn(
38
- process.execPath,
39
- cliMainArguments(runtime, fileURLToPath(mainUrl), process.argv.slice(2)),
40
- { stdio: "inherit" }
41
- );
57
+ const mainPath = fileURLToPath(mainUrl);
58
+ let argv = process.argv.slice(2);
59
+ for (; ; ) {
60
+ const code = await spawnAndWait(runtime, mainPath, argv);
61
+ if (code === MEMORY_RESTART_EXIT_CODE) {
62
+ let marker;
63
+ try {
64
+ marker = parseMemoryRestartMarker(readFileSync(memoryRestartMarkerPath(process.cwd()), "utf8"));
65
+ } catch {
66
+ marker = void 0;
67
+ }
68
+ const resumed = memoryRestartArgs(argv, marker);
69
+ if (resumed !== void 0) {
70
+ process.stderr.write(`flavor launcher: heap watermark restart, resuming session ${marker.sessionId}
71
+ `);
72
+ argv = resumed;
73
+ continue;
74
+ }
75
+ if (marker !== void 0) {
76
+ process.stderr.write("flavor launcher: heap watermark restart budget exhausted; restart manually and consider a fresh session.\n");
77
+ }
78
+ }
79
+ process.exitCode = code;
80
+ return;
81
+ }
82
+ }
83
+ async function spawnAndWait(runtime, mainPath, argv) {
84
+ const child = spawn(process.execPath, cliMainArguments(runtime, mainPath, argv), { stdio: "inherit" });
42
85
  const ignoreSignal = () => {
43
86
  };
44
87
  process.on("SIGINT", ignoreSignal);
45
88
  process.on("SIGBREAK", ignoreSignal);
46
89
  try {
47
- const code = await new Promise((resolvePromise, reject) => {
90
+ return await new Promise((resolvePromise, reject) => {
48
91
  child.once("error", reject);
49
92
  child.once("exit", (exitCode) => resolvePromise(exitCode ?? 1));
50
93
  });
51
- process.exitCode = code;
52
94
  } finally {
53
95
  process.off("SIGINT", ignoreSignal);
54
96
  process.off("SIGBREAK", ignoreSignal);
@@ -69,5 +111,6 @@ if (process.argv[1]) {
69
111
  export {
70
112
  cliMainArguments,
71
113
  launchCli,
114
+ needsRelaunch,
72
115
  needsWindowsMaglevWorkaround
73
116
  };
@@ -67,6 +67,13 @@ export type ContextForkOptions = Partial<Pick<ContextManagerOptions, "summarize"
67
67
  export { estimateTokens } from "./compaction.js";
68
68
  export declare class ContextManager {
69
69
  #private;
70
+ /**
71
+ * The visibility audit log persists every transient system admission and is
72
+ * serialized in full on every session save. Bound both dimensions or a long
73
+ * subagent-driven run grows it into hundreds of megabytes (heap OOM).
74
+ */
75
+ static readonly VISIBILITY_LOG_MAX_ENTRIES = 1000;
76
+ static readonly VISIBILITY_RECORD_CONTENT_LIMIT = 2048;
70
77
  constructor(options: ContextManagerOptions);
71
78
  clear(): void;
72
79
  /**