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.
@@ -2,7 +2,7 @@
2
2
  import { appendFileSync } from "fs";
3
3
  import { createHash as createHash28, randomUUID as randomUUID29 } from "crypto";
4
4
  import { readFile as readFile39, stat as stat18, writeFile as writeFile19 } from "fs/promises";
5
- import { basename as basename11, dirname as dirname24, join as join47, resolve as resolve40 } from "path";
5
+ import { basename as basename11, dirname as dirname25, join as join48, resolve as resolve40 } from "path";
6
6
  import { fileURLToPath as fileURLToPath4 } from "url";
7
7
  import { app, BrowserWindow as BrowserWindow2, clipboard, dialog, ipcMain, Menu, nativeImage, Notification, shell } from "electron";
8
8
 
@@ -305,7 +305,7 @@ var FlavorConfigSchema = z2.object({
305
305
  context: z2.object({
306
306
  windowTokens: z2.number().int().positive().default(2e5),
307
307
  reservedOutputTokens: z2.number().int().nonnegative().default(2e4),
308
- autoCompactBufferTokens: z2.number().int().nonnegative().default(13e3),
308
+ autoCompactBufferTokens: z2.number().int().nonnegative().default(27e3),
309
309
  warningBufferTokens: z2.number().int().nonnegative().default(2e4),
310
310
  blockingBufferTokens: z2.number().int().nonnegative().default(3e3),
311
311
  microcompactKeepRecentToolResults: z2.number().int().nonnegative().default(5),
@@ -4225,9 +4225,10 @@ ${summarizeReport(report2)}`).join("\n\n")
4225
4225
 
4226
4226
  // src/production.ts
4227
4227
  import { readFile as readFile27 } from "fs/promises";
4228
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync } from "fs";
4228
4229
  import { createHash as createHash21, randomUUID as randomUUID25 } from "crypto";
4229
4230
  import { homedir as homedir2, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
4230
- import { basename as basename8, join as join33, relative as relative23, resolve as resolve33, sep as sep23 } from "path";
4231
+ import { basename as basename8, dirname as dirname24, join as join34, relative as relative23, resolve as resolve33, sep as sep23 } from "path";
4231
4232
 
4232
4233
  // src/agent/subagents.ts
4233
4234
  import { z as z10 } from "zod";
@@ -5161,7 +5162,9 @@ import { createHash as createHash4, randomUUID as randomUUID6 } from "crypto";
5161
5162
  var DEFAULT_COMPACTION_POLICY = {
5162
5163
  windowTokens: 2e5,
5163
5164
  reservedOutputTokens: 2e4,
5164
- autoCompactBufferTokens: 13e3,
5165
+ // 85% of the effective window (180_000 - 27_000 = 153_000): compaction used
5166
+ // to trigger at ~92.8%, which let sessions ride the threshold into heap OOM.
5167
+ autoCompactBufferTokens: 27e3,
5165
5168
  warningBufferTokens: 2e4,
5166
5169
  blockingBufferTokens: 3e3,
5167
5170
  microcompactKeepRecentToolResults: 5,
@@ -5371,6 +5374,13 @@ var ContextManager = class _ContextManager {
5371
5374
  #epoch;
5372
5375
  #visibilityLog = [];
5373
5376
  #activeTransientSystem = /* @__PURE__ */ new Map();
5377
+ /**
5378
+ * The visibility audit log persists every transient system admission and is
5379
+ * serialized in full on every session save. Bound both dimensions or a long
5380
+ * subagent-driven run grows it into hundreds of megabytes (heap OOM).
5381
+ */
5382
+ static VISIBILITY_LOG_MAX_ENTRIES = 1e3;
5383
+ static VISIBILITY_RECORD_CONTENT_LIMIT = 2048;
5374
5384
  constructor(options) {
5375
5385
  if (options.compactAtChars !== void 0 && options.compactAtChars <= 0) throw new Error("compactAtChars must be positive");
5376
5386
  if (options.toolOutputChars <= 0) throw new Error("toolOutputChars must be positive");
@@ -5466,11 +5476,14 @@ var ContextManager = class _ContextManager {
5466
5476
  const record3 = {
5467
5477
  id,
5468
5478
  role: "system",
5469
- content,
5479
+ content: limitVisibilityContent(content),
5470
5480
  admittedAt: (/* @__PURE__ */ new Date()).toISOString(),
5471
5481
  scope: "run"
5472
5482
  };
5473
5483
  this.#visibilityLog.push(record3);
5484
+ if (this.#visibilityLog.length > _ContextManager.VISIBILITY_LOG_MAX_ENTRIES) {
5485
+ this.#visibilityLog = this.#visibilityLog.slice(-_ContextManager.VISIBILITY_LOG_MAX_ENTRIES);
5486
+ }
5474
5487
  this.#activeTransientSystem.set(id, { role: "system", content });
5475
5488
  return id;
5476
5489
  }
@@ -5494,8 +5507,9 @@ var ContextManager = class _ContextManager {
5494
5507
  const currentStableHash = currentStable === void 0 ? this.#epoch.stableSourceHash : hashMessages(currentStable);
5495
5508
  if (currentStable !== void 0 && currentStableHash !== this.#epoch.stableSourceHash) {
5496
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");
5497
5511
  if (this.#epoch.sources["system-baseline"] !== value) {
5498
- updates.push(contextUpdateMessage("system-baseline", value));
5512
+ updates.push(contextUpdateMessage("system-baseline", deltaSourceValue(previous, value)));
5499
5513
  this.#epoch.sources["system-baseline"] = value;
5500
5514
  }
5501
5515
  } else if (currentStable !== void 0 && this.#epoch.sources["system-baseline"] !== void 0) {
@@ -5506,7 +5520,7 @@ var ContextManager = class _ContextManager {
5506
5520
  for (const key of DYNAMIC_SOURCE_ORDER) {
5507
5521
  const value = next[key];
5508
5522
  if (value === void 0 || value === this.#epoch.sources[key]) continue;
5509
- updates.push(contextUpdateMessage(key, displaySourceValue(key, value)));
5523
+ updates.push(contextUpdateMessage(key, deltaSourceValue(this.#epoch.sources[key], displaySourceValue(key, value))));
5510
5524
  this.#epoch.sources[key] = value;
5511
5525
  }
5512
5526
  for (const key of DYNAMIC_SOURCE_ORDER) {
@@ -5533,7 +5547,7 @@ var ContextManager = class _ContextManager {
5533
5547
  const legacySummary = snapshot4.summary?.role === "system" && snapshot4.summary.content.startsWith("Conversation summary\n") ? snapshot4.summary.content.slice("Conversation summary\n".length) : void 0;
5534
5548
  this.#compact = snapshot4.compact === void 0 ? legacySummary === void 0 ? void 0 : { summary: legacySummary, compactedAt: (/* @__PURE__ */ new Date(0)).toISOString() } : { ...snapshot4.compact };
5535
5549
  if (snapshot4.epoch !== void 0) this.#epoch = cloneEpoch(snapshot4.epoch);
5536
- this.#visibilityLog = snapshot4.visibilityLog?.map((item) => ({ ...item })) ?? [];
5550
+ this.#visibilityLog = boundVisibilityLog(snapshot4.visibilityLog);
5537
5551
  this.#activeTransientSystem.clear();
5538
5552
  this.#messages = messages.map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
5539
5553
  this.#lastRecordedInputTokens = void 0;
@@ -5577,6 +5591,10 @@ var ContextManager = class _ContextManager {
5577
5591
  async prepareForModelCall(signal = new AbortController().signal) {
5578
5592
  signal.throwIfAborted();
5579
5593
  this.refreshContextSources();
5594
+ if (this.#dropStaleContextUpdates()) {
5595
+ this.#lastRecordedInputTokens = void 0;
5596
+ this.#estimatedTokensAtLastRecordedUsage = void 0;
5597
+ }
5580
5598
  if (!this.needsCompaction()) return false;
5581
5599
  const originalMessages = this.#messages;
5582
5600
  const originalRecordedUsage = this.#lastRecordedInputTokens;
@@ -5676,6 +5694,26 @@ var ContextManager = class _ContextManager {
5676
5694
  this.#reportCompactProgress(100);
5677
5695
  return true;
5678
5696
  }
5697
+ #dropStaleContextUpdates() {
5698
+ const latestBySource = /* @__PURE__ */ new Map();
5699
+ for (let index = this.#messages.length - 1; index >= 0; index -= 1) {
5700
+ const source = contextUpdateSource(this.#messages[index]);
5701
+ if (source === void 0 || latestBySource.has(source)) continue;
5702
+ latestBySource.set(source, index);
5703
+ }
5704
+ const next = [];
5705
+ let changed = false;
5706
+ this.#messages.forEach((message2, index) => {
5707
+ const source = contextUpdateSource(message2);
5708
+ if (source !== void 0 && latestBySource.get(source) !== index) {
5709
+ changed = true;
5710
+ return;
5711
+ }
5712
+ next.push(message2);
5713
+ });
5714
+ if (changed) this.#messages = next;
5715
+ return changed;
5716
+ }
5679
5717
  #reportCompactProgress(percentage) {
5680
5718
  const normalized = Math.max(0, Math.min(100, Math.floor(percentage / 10) * 10));
5681
5719
  if (normalized === this.#lastCompactProgress) return;
@@ -5787,6 +5825,33 @@ function contextUpdateMessage(source, content) {
5787
5825
  return { role: "system", content: `Context update [${source}]
5788
5826
  ${content}` };
5789
5827
  }
5828
+ var CONTEXT_UPDATE_PREFIX = "Context update [";
5829
+ function contextUpdateSource(message2) {
5830
+ if (message2.role !== "system" || typeof message2.content !== "string") return void 0;
5831
+ if (!message2.content.startsWith(CONTEXT_UPDATE_PREFIX)) return void 0;
5832
+ const end = message2.content.indexOf("]\n", CONTEXT_UPDATE_PREFIX.length);
5833
+ if (end < 0) return void 0;
5834
+ return message2.content.slice(CONTEXT_UPDATE_PREFIX.length, end);
5835
+ }
5836
+ function deltaSourceValue(previous, display) {
5837
+ if (previous === void 0 || !display.startsWith(previous)) return display;
5838
+ return display.slice(previous.length).replace(/^\s+/, "") || display;
5839
+ }
5840
+ function limitVisibilityContent(content) {
5841
+ const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
5842
+ if (content.length <= limit) return content;
5843
+ return `${content.slice(0, limit)}
5844
+ ...[truncated; original length: ${content.length} characters]...`;
5845
+ }
5846
+ function boundVisibilityLog(records) {
5847
+ if (records === void 0) return [];
5848
+ const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
5849
+ const bounded2 = records.map((item) => ({
5850
+ ...item,
5851
+ ...item.content.length <= limit ? {} : { content: limitVisibilityContent(item.content) }
5852
+ }));
5853
+ return bounded2.length > ContextManager.VISIBILITY_LOG_MAX_ENTRIES ? bounded2.slice(-ContextManager.VISIBILITY_LOG_MAX_ENTRIES) : bounded2;
5854
+ }
5790
5855
  function cloneEpoch(epoch) {
5791
5856
  return {
5792
5857
  ...epoch,
@@ -6081,6 +6146,9 @@ function reportProgress(callback, percentage) {
6081
6146
  }
6082
6147
  }
6083
6148
 
6149
+ // src/agent/loop.ts
6150
+ import { getHeapStatistics } from "v8";
6151
+
6084
6152
  // src/models/structured.ts
6085
6153
  import { z as z12 } from "zod";
6086
6154
  var DEFAULT_RETRY_DELAYS = [1e3, 2e3, 4e3];
@@ -6498,6 +6566,18 @@ function envMaxIterations() {
6498
6566
  if (!Number.isInteger(parsed) || parsed <= 0) return void 0;
6499
6567
  return parsed;
6500
6568
  }
6569
+ var MEMORY_PRESSURE_HEAP_RATIO = 0.8;
6570
+ function memoryPressureError(heapUsed, heapLimit) {
6571
+ if (heapLimit <= 0 || heapUsed < heapLimit * MEMORY_PRESSURE_HEAP_RATIO) return void 0;
6572
+ const mb = (bytes) => Math.round(bytes / 1048576);
6573
+ return {
6574
+ 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.`
6576
+ };
6577
+ }
6578
+ function currentMemoryPressure() {
6579
+ return memoryPressureError(process.memoryUsage().heapUsed, getHeapStatistics().heap_size_limit);
6580
+ }
6501
6581
  var AgentLoop = class {
6502
6582
  #options;
6503
6583
  #iterationLimitMode = "standard";
@@ -6568,6 +6648,11 @@ var AgentLoop = class {
6568
6648
  yield { type: "error", error: { code: "cancelled", message: abortMessage(request.signal) } };
6569
6649
  return;
6570
6650
  }
6651
+ const pressure = currentMemoryPressure();
6652
+ if (pressure !== void 0) {
6653
+ yield { type: "error", error: pressure };
6654
+ return;
6655
+ }
6571
6656
  const softLimit = Math.floor(maxIterations * this.#options.softLimitFactor);
6572
6657
  if (!warned && iteration >= softLimit) {
6573
6658
  warned = true;
@@ -12927,7 +13012,8 @@ var BoundedOutput = class {
12927
13012
  #headLimit;
12928
13013
  #tailLimit;
12929
13014
  #head = Buffer.alloc(0);
12930
- #tail = Buffer.alloc(0);
13015
+ #tailChunks = [];
13016
+ #tailBytes = 0;
12931
13017
  #complete = Buffer.alloc(0);
12932
13018
  #bytes = 0;
12933
13019
  constructor(limit) {
@@ -12947,15 +13033,24 @@ var BoundedOutput = class {
12947
13033
  const count = Math.min(this.#headLimit - this.#head.length, chunk.length);
12948
13034
  this.#head = Buffer.concat([this.#head, chunk.subarray(0, count)]);
12949
13035
  }
12950
- if (this.#tailLimit > 0) this.#tail = Buffer.concat([this.#tail, chunk]).subarray(-this.#tailLimit);
13036
+ if (this.#tailLimit > 0) {
13037
+ this.#tailChunks.push(chunk);
13038
+ this.#tailBytes += chunk.length;
13039
+ if (this.#tailBytes >= this.#tailLimit * 2) {
13040
+ const merged = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
13041
+ this.#tailChunks = [merged];
13042
+ this.#tailBytes = merged.length;
13043
+ }
13044
+ }
12951
13045
  }
12952
13046
  metadata() {
12953
13047
  return { truncated: this.truncated, originalBytes: this.#bytes, limitBytes: this.#limit };
12954
13048
  }
12955
13049
  text() {
12956
13050
  if (!this.truncated) return decodeOutput(this.#complete);
13051
+ const tail = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
12957
13052
  const encoding = classifyUtf8(this.#complete) === "invalid" && process.platform === "win32" ? "gb18030" : "utf-8";
12958
- return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(this.#tail, encoding)}`;
13053
+ return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(tail, encoding)}`;
12959
13054
  }
12960
13055
  };
12961
13056
  var AdaptiveOutputDecoder = class {
@@ -16774,11 +16869,35 @@ function isCode10(error, code) {
16774
16869
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
16775
16870
  }
16776
16871
 
16872
+ // src/utils/memory-restart.ts
16873
+ import { join as join26 } from "path";
16874
+ var MEMORY_RESTART_EXIT_CODE = 75;
16875
+ var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
16876
+ var MEMORY_RESTART_WINDOW_MS = 30 * 60 * 1e3;
16877
+ function memoryRestartMarkerPath(workspace) {
16878
+ return join26(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
16879
+ }
16880
+ function nextMemoryRestartMarker(previous, sessionId, now) {
16881
+ 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;
16882
+ return { sessionId, requestedAt: now.toISOString(), attempts: reusable ? previous.attempts + 1 : 1 };
16883
+ }
16884
+ function parseMemoryRestartMarker(json) {
16885
+ try {
16886
+ const value = JSON.parse(json);
16887
+ if (typeof value.sessionId !== "string" || typeof value.requestedAt !== "string" || typeof value.attempts !== "number") {
16888
+ return void 0;
16889
+ }
16890
+ return { sessionId: value.sessionId, requestedAt: value.requestedAt, attempts: value.attempts };
16891
+ } catch {
16892
+ return void 0;
16893
+ }
16894
+ }
16895
+
16777
16896
  // src/sleep/organizer.ts
16778
16897
  import { constants as constants7 } from "fs";
16779
16898
  import { randomUUID as randomUUID16 } from "crypto";
16780
16899
  import { lstat as lstat10, mkdir as mkdir18, open as open9, readdir as readdir8, realpath as realpath11, rename as rename9, rm as rm10 } from "fs/promises";
16781
- import { isAbsolute as isAbsolute10, join as join26, relative as relative15, resolve as resolve22, sep as sep15 } from "path";
16900
+ import { isAbsolute as isAbsolute10, join as join27, relative as relative15, resolve as resolve22, sep as sep15 } from "path";
16782
16901
  import { z as z28 } from "zod";
16783
16902
  var DateKeySchema = z28.string().regex(/^\d{4}-\d{2}-\d{2}$/);
16784
16903
  var ReviewSchema = z28.object({
@@ -16833,7 +16952,7 @@ var ProjectSleepOrganizer = class {
16833
16952
  #generate;
16834
16953
  constructor(options) {
16835
16954
  this.#workspace = resolve22(options.workspace);
16836
- this.#sleepDirectory = join26(this.#workspace, ".flavor", "sleep");
16955
+ this.#sleepDirectory = join27(this.#workspace, ".flavor", "sleep");
16837
16956
  this.#sessions = options.sessions ?? new SessionStore({ workspace: this.#workspace });
16838
16957
  this.#generate = options.generate;
16839
16958
  }
@@ -16842,7 +16961,7 @@ var ProjectSleepOrganizer = class {
16842
16961
  signal.throwIfAborted();
16843
16962
  await this.#prepareDirectory();
16844
16963
  if (await this.#hasReport(date)) return { status: "exists", date };
16845
- const lockPath = join26(this.#sleepDirectory, `.${date}.lock`);
16964
+ const lockPath = join27(this.#sleepDirectory, `.${date}.lock`);
16846
16965
  let lock;
16847
16966
  try {
16848
16967
  lock = await open9(lockPath, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
@@ -16877,7 +16996,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
16877
16996
  }
16878
16997
  if (review === void 0) throw lastError;
16879
16998
  const filename2 = `${date}-${filenameSummary(review.title)}.md`;
16880
- const path = join26(this.#sleepDirectory, filename2);
16999
+ const path = join27(this.#sleepDirectory, filename2);
16881
17000
  const markdown = renderSleepReport(date, review, documents);
16882
17001
  await this.#atomicWrite(path, markdown);
16883
17002
  return { status: "written", date, path, sessionCount: documents.length };
@@ -16919,7 +17038,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
16919
17038
  });
16920
17039
  }
16921
17040
  async #prepareDirectory() {
16922
- const flavorDirectory = join26(this.#workspace, ".flavor");
17041
+ const flavorDirectory = join27(this.#workspace, ".flavor");
16923
17042
  await assertNotSymlink(flavorDirectory);
16924
17043
  await mkdir18(flavorDirectory, { recursive: true, mode: 448 });
16925
17044
  await assertNotSymlink(this.#sleepDirectory);
@@ -16933,7 +17052,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
16933
17052
  return (await readdir8(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
16934
17053
  }
16935
17054
  async #atomicWrite(path, content) {
16936
- const temporary = join26(this.#sleepDirectory, `.${randomUUID16()}.tmp`);
17055
+ const temporary = join27(this.#sleepDirectory, `.${randomUUID16()}.tmp`);
16937
17056
  let handle;
16938
17057
  try {
16939
17058
  handle = await open9(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
@@ -19837,12 +19956,12 @@ ${explanation}` : "";
19837
19956
  // src/desktop/astgraph-service.ts
19838
19957
  import { stat as stat10 } from "fs/promises";
19839
19958
  import { createRequire as nodeCreateRequire } from "module";
19840
- import { join as join27, resolve as resolve26 } from "path";
19959
+ import { join as join28, resolve as resolve26 } from "path";
19841
19960
  var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
19842
19961
  var DesktopAstGraphService = class {
19843
19962
  #path;
19844
19963
  constructor(workspace) {
19845
- this.#path = join27(resolve26(workspace), ".flavor", "astgraph", "index.db");
19964
+ this.#path = join28(resolve26(workspace), ".flavor", "astgraph", "index.db");
19846
19965
  }
19847
19966
  async status() {
19848
19967
  if ((await stat10(this.#path).catch(() => void 0))?.isFile() !== true) {
@@ -20111,7 +20230,7 @@ function createTodoWriteTool() {
20111
20230
  // src/tools/managed.ts
20112
20231
  import { randomUUID as randomUUID18 } from "crypto";
20113
20232
  import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile22, readdir as readdir9, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
20114
- import { dirname as dirname21, join as join28, resolve as resolve27 } from "path";
20233
+ import { dirname as dirname21, join as join29, resolve as resolve27 } from "path";
20115
20234
  import { z as z37 } from "zod";
20116
20235
  var MANAGED_TOOL_VERSION = 1;
20117
20236
  var MAX_RECORD_BYTES = 256 * 1024;
@@ -20154,8 +20273,8 @@ var ManagedToolStore = class {
20154
20273
  #diagnostics = [];
20155
20274
  constructor(options) {
20156
20275
  this.#workspace = resolve27(options.workspace);
20157
- this.#projectRoot = join28(this.#workspace, ".flavor", "tools");
20158
- this.#globalRoot = join28(resolve27(options.home), ".flavor-code", "tools");
20276
+ this.#projectRoot = join29(this.#workspace, ".flavor", "tools");
20277
+ this.#globalRoot = join29(resolve27(options.home), ".flavor-code", "tools");
20159
20278
  this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
20160
20279
  }
20161
20280
  get diagnostics() {
@@ -20202,7 +20321,7 @@ var ManagedToolStore = class {
20202
20321
  version: MANAGED_TOOL_VERSION,
20203
20322
  createdAt: this.#now()
20204
20323
  });
20205
- const path = join28(this.directory(input.scope), filename(input.name));
20324
+ const path = join29(this.directory(input.scope), filename(input.name));
20206
20325
  const definition = compileDefinition(record3, input.scope, this.#workspace);
20207
20326
  await writeExclusive(path, `${JSON.stringify(record3, void 0, 2)}
20208
20327
  `);
@@ -20227,7 +20346,7 @@ var ManagedToolStore = class {
20227
20346
  if (!parsed.success) return [];
20228
20347
  const matches = this.#matching(parsed.data.name).filter((entry2) => parsed.data.scope === void 0 || entry2.scope === parsed.data.scope);
20229
20348
  if (matches.length > 0) return matches.map((entry2) => entry2.path);
20230
- return [join28(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
20349
+ return [join29(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
20231
20350
  }
20232
20351
  async #loadDirectory(scope2) {
20233
20352
  const root = this.directory(scope2);
@@ -20240,7 +20359,7 @@ var ManagedToolStore = class {
20240
20359
  return;
20241
20360
  }
20242
20361
  for (const name of names) {
20243
- const path = join28(root, name);
20362
+ const path = join29(root, name);
20244
20363
  try {
20245
20364
  const info = await lstat11(path);
20246
20365
  if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
@@ -22558,7 +22677,7 @@ ${total}`;
22558
22677
  // src/memory/store.ts
22559
22678
  import { createHash as createHash17 } from "crypto";
22560
22679
  import { readFile as readFile23, rm as rm12 } from "fs/promises";
22561
- import { resolve as resolve28, join as join29, relative as relative19, sep as sep19 } from "path";
22680
+ import { resolve as resolve28, join as join30, relative as relative19, sep as sep19 } from "path";
22562
22681
 
22563
22682
  // src/memory/similarity.ts
22564
22683
  function normalizeForSimilarity(value) {
@@ -22661,9 +22780,9 @@ var MemoryStore = class {
22661
22780
  if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
22662
22781
  if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
22663
22782
  this.workspace = resolve28(options.workspace);
22664
- this.#memoryRoot = join29(this.workspace, ".flavor", "memory");
22665
- this.path = join29(this.#memoryRoot, "MEMORY.md");
22666
- this.#behaviorPath = join29(this.#memoryRoot, "behavior.json");
22783
+ this.#memoryRoot = join30(this.workspace, ".flavor", "memory");
22784
+ this.path = join30(this.#memoryRoot, "MEMORY.md");
22785
+ this.#behaviorPath = join30(this.#memoryRoot, "behavior.json");
22667
22786
  this.#maxEntries = options.maxEntries;
22668
22787
  this.#maxEntryChars = options.maxEntryChars;
22669
22788
  }
@@ -23452,7 +23571,7 @@ function createExecutionEnvironment(workspace, config) {
23452
23571
 
23453
23572
  // src/ide/client.ts
23454
23573
  import { readFile as readFile24, readdir as readdir10, stat as stat11 } from "fs/promises";
23455
- import { basename as basename7, join as join30, relative as relative21, resolve as resolve30, sep as sep21 } from "path";
23574
+ import { basename as basename7, join as join31, relative as relative21, resolve as resolve30, sep as sep21 } from "path";
23456
23575
  var FlavorIdeClient = class {
23457
23576
  #workspace;
23458
23577
  #home;
@@ -23548,7 +23667,7 @@ var FlavorIdeClient = class {
23548
23667
  };
23549
23668
  if (await this.#healthy(configured)) return configured;
23550
23669
  }
23551
- const directory = join30(this.#home, ".flavor-code", "ide");
23670
+ const directory = join31(this.#home, ".flavor-code", "ide");
23552
23671
  let entries;
23553
23672
  try {
23554
23673
  entries = (await readdir10(directory)).filter((name) => name.endsWith(".lock"));
@@ -23556,7 +23675,7 @@ var FlavorIdeClient = class {
23556
23675
  return void 0;
23557
23676
  }
23558
23677
  const candidates = (await Promise.all(entries.map(async (name) => {
23559
- const path = join30(directory, name);
23678
+ const path = join31(directory, name);
23560
23679
  try {
23561
23680
  const [contents, info] = await Promise.all([readFile24(path, "utf8"), stat11(path)]);
23562
23681
  const lock = parseLockfile(contents);
@@ -24063,7 +24182,7 @@ function palSocketAddress(options) {
24063
24182
  import { createHash as createHash20 } from "crypto";
24064
24183
  import { Socket as Socket2 } from "net";
24065
24184
  import { tmpdir as tmpdir3 } from "os";
24066
- import { join as join32 } from "path";
24185
+ import { join as join33 } from "path";
24067
24186
  import { spawn as spawn7 } from "child_process";
24068
24187
 
24069
24188
  // src/pals/broker.ts
@@ -24076,16 +24195,16 @@ import { dirname as dirname23 } from "path";
24076
24195
  import { randomBytes as randomBytes3, randomUUID as randomUUID21 } from "crypto";
24077
24196
  import { chmod as chmod2, link as link2, mkdir as mkdir21, open as open12, readFile as readFile25, unlink as unlink4 } from "fs/promises";
24078
24197
  import { homedir } from "os";
24079
- import { join as join31 } from "path";
24198
+ import { join as join32 } from "path";
24080
24199
  var PAL_AUTH_TOKEN_BYTES = 32;
24081
24200
  var TOKEN_PATTERN = /^[a-f0-9]{64}$/;
24082
24201
  async function loadOrCreatePalAuthToken(options = {}) {
24083
- const directory = join31(options.home ?? homedir(), ".flavor-code", "pals");
24084
- const tokenPath = join31(directory, "auth-token");
24202
+ const directory = join32(options.home ?? homedir(), ".flavor-code", "pals");
24203
+ const tokenPath = join32(directory, "auth-token");
24085
24204
  await mkdir21(directory, { recursive: true, mode: 448 });
24086
24205
  await chmod2(directory, 448);
24087
24206
  const token = (options.random ?? randomBytes3)(PAL_AUTH_TOKEN_BYTES).toString("hex");
24088
- const temporaryPath = join31(directory, `.auth-token-${process.pid}-${randomUUID21()}.tmp`);
24207
+ const temporaryPath = join32(directory, `.auth-token-${process.pid}-${randomUUID21()}.tmp`);
24089
24208
  let handle;
24090
24209
  try {
24091
24210
  handle = await open12(temporaryPath, "wx", 384);
@@ -24215,7 +24334,7 @@ function canConnect(address) {
24215
24334
  }
24216
24335
  function startupLockPath(address) {
24217
24336
  const digest = createHash20("sha256").update(address, "utf8").digest("hex").slice(0, 24);
24218
- return join32(tmpdir3(), `flavor-code-pals-${digest}.startup.lock`);
24337
+ return join33(tmpdir3(), `flavor-code-pals-${digest}.startup.lock`);
24219
24338
  }
24220
24339
  async function spawnProductionBroker(address) {
24221
24340
  const entry2 = process.argv[1];
@@ -25151,8 +25270,8 @@ async function createProductionRuntime(options) {
25151
25270
  const registeredProviders = registration.registered;
25152
25271
  let effectiveLlm = registration.effectiveLlm;
25153
25272
  const pluginHost = new PluginHost({
25154
- globalPluginDirs: [join33(home, ".flavor-code", "plugins")],
25155
- projectPluginDirs: [join33(workspace, ".flavor", "plugins")],
25273
+ globalPluginDirs: [join34(home, ".flavor-code", "plugins")],
25274
+ projectPluginDirs: [join34(workspace, ".flavor", "plugins")],
25156
25275
  config,
25157
25276
  // Existing project plugins (including astgraph and superharness) use Node.js
25158
25277
  // built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
@@ -25260,7 +25379,7 @@ async function createProductionRuntime(options) {
25260
25379
  const address = palSocketAddress({
25261
25380
  platform: process.platform,
25262
25381
  userScope: home,
25263
- runtimeDir: join33(tmpdir4(), `flavor-code-pals-${createHash21("sha256").update(home).digest("hex").slice(0, 16)}`)
25382
+ runtimeDir: join34(tmpdir4(), `flavor-code-pals-${createHash21("sha256").update(home).digest("hex").slice(0, 16)}`)
25264
25383
  });
25265
25384
  const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
25266
25385
  instanceId: collaboration.instanceId,
@@ -25371,8 +25490,8 @@ async function createProductionRuntime(options) {
25371
25490
  diagnostics.push(`MCP servers could not start: ${message(error)}`);
25372
25491
  });
25373
25492
  const skills = new SkillRegistry({
25374
- globalRoots: [join33(home, ".flavor-code", "skills")],
25375
- projectRoots: [join33(workspace, ".flavor", "skills"), ...pluginSkillRoots],
25493
+ globalRoots: [join34(home, ".flavor-code", "skills")],
25494
+ projectRoots: [join34(workspace, ".flavor", "skills"), ...pluginSkillRoots],
25376
25495
  authorizeResource: async () => true,
25377
25496
  disabledNames: config.skills.disabled
25378
25497
  });
@@ -25380,7 +25499,7 @@ async function createProductionRuntime(options) {
25380
25499
  skillsReady.catch(() => void 0);
25381
25500
  tools.push(createSkillTool(skills), createSkillResourceTool(skills));
25382
25501
  if (options.extraTools !== void 0) tools.push(...options.extraTools);
25383
- const flavor = await optionalText2(join33(workspace, "FLAVOR.md"));
25502
+ const flavor = await optionalText2(join34(workspace, "FLAVOR.md"));
25384
25503
  const instructionBaseline = await workspaceInstructions.baseline();
25385
25504
  let memoryContext;
25386
25505
  if (memoryStore !== void 0) {
@@ -25961,7 +26080,7 @@ async function createProductionRuntime(options) {
25961
26080
  evolveService.toolDefinition(),
25962
26081
  ...mcpTools
25963
26082
  ];
25964
- const loopFlavor = await optionalText2(join33(input.workspace, "FLAVOR.md"));
26083
+ const loopFlavor = await optionalText2(join34(input.workspace, "FLAVOR.md"));
25965
26084
  const loopInstructionBaseline = await loopInstructions.baseline();
25966
26085
  const loopEnvironment = createPromptEnvironment({
25967
26086
  now: /* @__PURE__ */ new Date(),
@@ -26206,7 +26325,7 @@ async function createProductionRuntime(options) {
26206
26325
  contextEpoch: harness.main.context.snapshot().epoch?.id ?? "legacy"
26207
26326
  });
26208
26327
  const turnId = harnessJournal.startTurn(turnConfig, { prompt, initialUserMessage: runOptions?.initialUserMessage });
26209
- return durableTurn(persistAndCheckpointAfter(runMain(
26328
+ return monitorMemoryRestart(durableTurn(persistAndCheckpointAfter(runMain(
26210
26329
  harness,
26211
26330
  skills,
26212
26331
  prompt,
@@ -26226,7 +26345,7 @@ async function createProductionRuntime(options) {
26226
26345
  prompt,
26227
26346
  context: harness.main.context.snapshot(),
26228
26347
  label: `turn: ${prompt.slice(0, 80)}`
26229
- })), harnessJournal, turnId, turnConfig);
26348
+ })), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
26230
26349
  },
26231
26350
  runSkill: (skill, prompt, signal) => persistAfter(
26232
26351
  runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
@@ -26526,7 +26645,7 @@ ${renderMemoryDocument(entries)}`;
26526
26645
  return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
26527
26646
  }
26528
26647
  try {
26529
- const tokenStore = createFileTokenStore(join33(home, ".flavor-code", "auth.json"));
26648
+ const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
26530
26649
  const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
26531
26650
  await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
26532
26651
  const oauth = new OAuthCallbackAuthProvider({
@@ -26573,7 +26692,7 @@ ${renderMemoryDocument(entries)}`;
26573
26692
  }
26574
26693
  },
26575
26694
  async logout() {
26576
- const tokenStore = createFileTokenStore(join33(home, ".flavor-code", "auth.json"));
26695
+ const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
26577
26696
  const tokens2 = await tokenStore.load();
26578
26697
  const hadCredentials = Object.keys(tokens2).length > 0 || effectiveLlm !== void 0;
26579
26698
  if (!hadCredentials) {
@@ -26975,6 +27094,31 @@ async function* durableTurn(source, journal, turnId, config) {
26975
27094
  }
26976
27095
  }
26977
27096
  }
27097
+ async function* monitorMemoryRestart(source, onRestartNeeded) {
27098
+ let pressure = false;
27099
+ for await (const event of source) {
27100
+ if (event.type === "error" && event.error.code === "memory_pressure") pressure = true;
27101
+ yield event;
27102
+ }
27103
+ if (pressure) onRestartNeeded();
27104
+ }
27105
+ function requestMemoryRestart(workspace, sessionId, emitOutput) {
27106
+ try {
27107
+ const markerPath = memoryRestartMarkerPath(workspace);
27108
+ let previous;
27109
+ try {
27110
+ previous = parseMemoryRestartMarker(readFileSync3(markerPath, "utf8"));
27111
+ } catch {
27112
+ }
27113
+ mkdirSync3(dirname24(markerPath), { recursive: true });
27114
+ writeFileSync(markerPath, `${JSON.stringify(nextMemoryRestartMarker(previous, sessionId, /* @__PURE__ */ new Date()))}
27115
+ `, "utf8");
27116
+ } catch {
27117
+ }
27118
+ process.exitCode = MEMORY_RESTART_EXIT_CODE;
27119
+ emitOutput({ type: "notice", message: "Memory watermark reached; the session is saved and Flavor is restarting with a fresh heap." });
27120
+ emitOutput({ type: "exit" });
27121
+ }
26978
27122
  async function runGitCommit(deps, hint, signal) {
26979
27123
  if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
26980
27124
  let summary2 = await changeSummary(deps.workspace);
@@ -27068,7 +27212,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
27068
27212
  const configured = { ...providers };
27069
27213
  if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
27070
27214
  if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
27071
- const oauthTokenStore = createFileTokenStore(join33(home, ".flavor-code", "auth.json"));
27215
+ const oauthTokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
27072
27216
  const registered = [];
27073
27217
  let effectiveLlm;
27074
27218
  for (const [name, provider] of Object.entries(configured)) {
@@ -27501,11 +27645,11 @@ function createMemoryTaskId() {
27501
27645
  import { createHash as createHash27 } from "crypto";
27502
27646
  import { mkdir as mkdir27, readFile as readFile35, readdir as readdir12, rm as rm15, stat as stat15, writeFile as writeFile17 } from "fs/promises";
27503
27647
  import { homedir as homedir4 } from "os";
27504
- import { basename as basename9, join as join43, resolve as resolve36 } from "path";
27648
+ import { basename as basename9, join as join44, resolve as resolve36 } from "path";
27505
27649
 
27506
27650
  // src/d2c/workflow.ts
27507
27651
  import { createHash as createHash22 } from "crypto";
27508
- import { join as join34 } from "path";
27652
+ import { join as join35 } from "path";
27509
27653
  import { z as z42 } from "zod";
27510
27654
 
27511
27655
  // src/d2c/workflow-shared.ts
@@ -27651,7 +27795,7 @@ function reviewsFor(report, previous) {
27651
27795
  function implementationDirectory(report) {
27652
27796
  const source = report.implementation.source;
27653
27797
  if (!/^https?:\/\//i.test(source)) return source;
27654
- return join34("src", "d2c-output", report.task);
27798
+ return join35("src", "d2c-output", report.task);
27655
27799
  }
27656
27800
  function createWorkflow(report, framework) {
27657
27801
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -27775,7 +27919,7 @@ function applyQualityIssueDecision(workflow, issueId2, decision) {
27775
27919
  };
27776
27920
  }
27777
27921
  function workflowPath(workspace, task) {
27778
- return join34(taskDir(workspace, task), "workflow.json");
27922
+ return join35(taskDir(workspace, task), "workflow.json");
27779
27923
  }
27780
27924
  async function readWorkflow(workspace, task) {
27781
27925
  return (await readRecoverableFile(workflowPath(workspace, task), decodeWorkflow))?.value;
@@ -28029,7 +28173,7 @@ function confirmApiMapping(mapping, operationKey, operations) {
28029
28173
 
28030
28174
  // src/d2c/modules.ts
28031
28175
  import { readFile as readFile28 } from "fs/promises";
28032
- import { isAbsolute as isAbsolute15, join as join35, normalize as normalize2, posix, sep as sep24, win32 as win322 } from "path";
28176
+ import { isAbsolute as isAbsolute15, join as join36, normalize as normalize2, posix, sep as sep24, win32 as win322 } from "path";
28033
28177
  import { z as z43 } from "zod";
28034
28178
  var ModuleSchema = z43.object({
28035
28179
  id: z43.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
@@ -28048,13 +28192,13 @@ function safeSource(path) {
28048
28192
  }
28049
28193
  function d2cOutputDirectory(workspace, task) {
28050
28194
  if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
28051
- return join35(workspace, "src", "d2c-output", task);
28195
+ return join36(workspace, "src", "d2c-output", task);
28052
28196
  }
28053
28197
  async function readD2cModules(workspace, task) {
28054
28198
  const project = d2cOutputDirectory(workspace, task);
28055
28199
  let raw;
28056
28200
  try {
28057
- raw = await readFile28(join35(project, "d2c.modules.json"), "utf8");
28201
+ raw = await readFile28(join36(project, "d2c.modules.json"), "utf8");
28058
28202
  } catch {
28059
28203
  return [{ id: "page", label: task, sourceFiles: ["src/App.vue", "src/App.jsx"], keywords: [task] }];
28060
28204
  }
@@ -28075,7 +28219,7 @@ async function readD2cModules(workspace, task) {
28075
28219
 
28076
28220
  // src/d2c/integration.ts
28077
28221
  import { mkdir as mkdir24, readFile as readFile29, writeFile as writeFile13 } from "fs/promises";
28078
- import { join as join36 } from "path";
28222
+ import { join as join37 } from "path";
28079
28223
  function record2(value) {
28080
28224
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
28081
28225
  }
@@ -28211,7 +28355,7 @@ async def reset_acceptance(request: Request):
28211
28355
  }
28212
28356
  async function generateIntegrationArtifacts(projectDir, document, mappings, options = {}) {
28213
28357
  if (mappings.some((mapping) => mapping.status === "needs-confirmation")) throw new Error("Confirm every uncertain OpenAPI mapping before generating integration code");
28214
- const packagePath = join36(projectDir, "package.json");
28358
+ const packagePath = join37(projectDir, "package.json");
28215
28359
  let pkg;
28216
28360
  try {
28217
28361
  pkg = JSON.parse(await readFile29(packagePath, "utf8"));
@@ -28221,9 +28365,9 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
28221
28365
  const scripts = options.pythonServer ? { ...record2(pkg.scripts) ?? {} } : { ...record2(pkg.scripts) ?? {}, mock: "node mock/server.mjs" };
28222
28366
  const dependencies2 = options.pythonServer ? { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9" } : { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9", express: "^5.2.1" };
28223
28367
  const selected = [...new Set(mappings.map((mapping) => mapping.operationKey))].map((key) => document.operations.find((operation) => operation.id === key)).filter((operation) => operation !== void 0);
28224
- const apiDir = join36(projectDir, "src", "api");
28225
- const mockDir = join36(projectDir, "mock");
28226
- const serverDir = join36(projectDir, "server");
28368
+ const apiDir = join37(projectDir, "src", "api");
28369
+ const mockDir = join37(projectDir, "mock");
28370
+ const serverDir = join37(projectDir, "server");
28227
28371
  await Promise.all([
28228
28372
  mkdir24(apiDir, { recursive: true }),
28229
28373
  ...options.pythonServer ? [] : [mkdir24(mockDir, { recursive: true })],
@@ -28233,23 +28377,23 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
28233
28377
  if (options.pythonServer) files.push("server/main.py", "server/requirements.txt", "server/README.md", "server/flavor-runtime.json");
28234
28378
  else files.push("mock/server.mjs");
28235
28379
  await Promise.all([
28236
- writeFile13(join36(apiDir, "http.js"), `import axios from "axios";
28380
+ writeFile13(join37(apiDir, "http.js"), `import axios from "axios";
28237
28381
 
28238
28382
  export const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || ${JSON.stringify(document.baseUrl ?? "")}, timeout: 15000 });
28239
28383
 
28240
28384
  http.interceptors.response.use((response) => response, (error) => Promise.reject({ name: "ApiError", status: error.response?.status, message: error.response?.data?.message || error.message, cause: error }));
28241
28385
  `),
28242
- writeFile13(join36(apiDir, "d2c-api.js"), apiModule(selected)),
28243
- writeFile13(join36(apiDir, "d2c-bindings.json"), `${JSON.stringify({ schema: 1, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), mappings }, null, 2)}
28386
+ writeFile13(join37(apiDir, "d2c-api.js"), apiModule(selected)),
28387
+ writeFile13(join37(apiDir, "d2c-bindings.json"), `${JSON.stringify({ schema: 1, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), mappings }, null, 2)}
28244
28388
  `),
28245
- ...options.pythonServer ? [] : [writeFile13(join36(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
28389
+ ...options.pythonServer ? [] : [writeFile13(join37(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
28246
28390
  writeFile13(packagePath, `${JSON.stringify({ ...pkg, scripts, dependencies: dependencies2 }, null, 2)}
28247
28391
  `),
28248
28392
  ...options.pythonServer ? [
28249
- writeFile13(join36(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
28250
- writeFile13(join36(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
28251
- writeFile13(join36(serverDir, "README.md"), "# Python API service\n\nThis is the real integration service, not a mock. Development defaults to SQLite through `DATABASE_URL=sqlite:///./data/app.db`. Set the same environment variable to a SQLAlchemy MySQL or PostgreSQL URL for deployment; keep schema changes in migrations instead of database-specific SQL. Flavor Code starts the service automatically for acceptance.\n"),
28252
- writeFile13(join36(serverDir, "flavor-runtime.json"), `${JSON.stringify({
28393
+ writeFile13(join37(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
28394
+ writeFile13(join37(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
28395
+ writeFile13(join37(serverDir, "README.md"), "# Python API service\n\nThis is the real integration service, not a mock. Development defaults to SQLite through `DATABASE_URL=sqlite:///./data/app.db`. Set the same environment variable to a SQLAlchemy MySQL or PostgreSQL URL for deployment; keep schema changes in migrations instead of database-specific SQL. Flavor Code starts the service automatically for acceptance.\n"),
28396
+ writeFile13(join37(serverDir, "flavor-runtime.json"), `${JSON.stringify({
28253
28397
  schema: 1,
28254
28398
  kind: "python-fastapi",
28255
28399
  cwd: "server",
@@ -28270,7 +28414,7 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
28270
28414
  import { spawn as spawn8 } from "child_process";
28271
28415
  import { access as access3, readFile as readFile30 } from "fs/promises";
28272
28416
  import { createServer as createServer5 } from "net";
28273
- import { join as join37 } from "path";
28417
+ import { join as join38 } from "path";
28274
28418
  async function exists3(path) {
28275
28419
  try {
28276
28420
  await access3(path);
@@ -28378,7 +28522,7 @@ ${output.trim()}` : ""}`);
28378
28522
  }
28379
28523
  }
28380
28524
  async function runPythonBackend(projectDir, options) {
28381
- const serverDir = join37(projectDir, "server");
28525
+ const serverDir = join38(projectDir, "server");
28382
28526
  if (options.installDependencies !== false) await installPython(serverDir, options.signal);
28383
28527
  const selectedPort = await port();
28384
28528
  const url = `http://127.0.0.1:${selectedPort}`;
@@ -28424,7 +28568,7 @@ ${managed.output().slice(-4e3)}`);
28424
28568
  };
28425
28569
  }
28426
28570
  async function runD2cMockServer(projectDir, options = {}) {
28427
- const runtimePath = join37(projectDir, "server", "flavor-runtime.json");
28571
+ const runtimePath = join38(projectDir, "server", "flavor-runtime.json");
28428
28572
  if (await exists3(runtimePath)) {
28429
28573
  let runtime;
28430
28574
  try {
@@ -28435,9 +28579,9 @@ async function runD2cMockServer(projectDir, options = {}) {
28435
28579
  if (runtime.schema !== 1 || runtime.kind !== "python-fastapi") throw new Error("Unsupported E2E backend runtime manifest");
28436
28580
  return runPythonBackend(projectDir, options);
28437
28581
  }
28438
- const serverPath = join37(projectDir, "mock", "server.mjs");
28582
+ const serverPath = join38(projectDir, "mock", "server.mjs");
28439
28583
  if (!await exists3(serverPath)) throw new Error(`D2C mock server is not generated at ${serverPath}`);
28440
- if (options.installDependencies === false && !await exists3(join37(projectDir, "node_modules", "express", "package.json"))) {
28584
+ if (options.installDependencies === false && !await exists3(join38(projectDir, "node_modules", "express", "package.json"))) {
28441
28585
  throw new Error("D2C mock server dependencies are not installed");
28442
28586
  }
28443
28587
  if (options.installDependencies !== false) await install(projectDir, options.signal);
@@ -28480,7 +28624,7 @@ ${managed.output().slice(-4e3)}`);
28480
28624
  // src/d2c/product.ts
28481
28625
  import { createHash as createHash24, randomUUID as randomUUID27 } from "crypto";
28482
28626
  import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile32, rename as rename13, stat as stat13, unlink as unlink7, writeFile as writeFile15 } from "fs/promises";
28483
- import { join as join38 } from "path";
28627
+ import { join as join39 } from "path";
28484
28628
  import { z as z44 } from "zod";
28485
28629
 
28486
28630
  // src/e2e/prd-governance.ts
@@ -28635,13 +28779,13 @@ var PlanSchema2 = z44.object({
28635
28779
  }).strict();
28636
28780
  function d2cProductDirectory(workspace, task) {
28637
28781
  if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
28638
- return join38(taskDir(workspace, task), "product");
28782
+ return join39(taskDir(workspace, task), "product");
28639
28783
  }
28640
28784
  function d2cProductPrototypeDirectory(workspace, task) {
28641
- return join38(d2cProductDirectory(workspace, task), "prototype");
28785
+ return join39(d2cProductDirectory(workspace, task), "prototype");
28642
28786
  }
28643
28787
  function planPath(workspace, task) {
28644
- return join38(taskDir(workspace, task), "product-plan.json");
28788
+ return join39(taskDir(workspace, task), "product-plan.json");
28645
28789
  }
28646
28790
  function resolveD2cProductTechnology(requirement, fallbackFramework = "vue") {
28647
28791
  const frontendCandidates = [
@@ -28824,16 +28968,16 @@ async function readD2cProductPlan(workspace, task) {
28824
28968
  }
28825
28969
  async function discoverD2cProductArtifacts(workspace, plan) {
28826
28970
  const product = d2cProductDirectory(workspace, plan.task);
28827
- const prdUpdatedAt = await fileTimestamp(join38(product, "prd.md"));
28828
- const prdHash = await fileHash(join38(product, "prd.md"));
28829
- const prototypeUpdatedAt = await fileTimestamp(join38(product, "prototype", "index.html"));
28830
- const prototypeHash = await fileHash(join38(product, "prototype", "index.html"));
28831
- const expectedManifest = join38(product, "prototype", "interaction-manifest.json");
28971
+ const prdUpdatedAt = await fileTimestamp(join39(product, "prd.md"));
28972
+ const prdHash = await fileHash(join39(product, "prd.md"));
28973
+ const prototypeUpdatedAt = await fileTimestamp(join39(product, "prototype", "index.html"));
28974
+ const prototypeHash = await fileHash(join39(product, "prototype", "index.html"));
28975
+ const expectedManifest = join39(product, "prototype", "interaction-manifest.json");
28832
28976
  let manifestUpdatedAt = await fileTimestamp(expectedManifest);
28833
28977
  if (manifestUpdatedAt === void 0) {
28834
- const misplacedManifest = join38(product, "interaction-manifest.json");
28978
+ const misplacedManifest = join39(product, "interaction-manifest.json");
28835
28979
  if (await fileTimestamp(misplacedManifest) !== void 0) {
28836
- await mkdir25(join38(product, "prototype"), { recursive: true });
28980
+ await mkdir25(join39(product, "prototype"), { recursive: true });
28837
28981
  await copyFile2(misplacedManifest, expectedManifest);
28838
28982
  manifestUpdatedAt = await fileTimestamp(expectedManifest);
28839
28983
  }
@@ -28866,8 +29010,8 @@ async function readD2cProductPlanView(workspace, task) {
28866
29010
  if (stored === void 0) return void 0;
28867
29011
  const plan = await discoverD2cProductArtifacts(workspace, stored);
28868
29012
  if (plan.revision !== stored.revision) await writeD2cProductPlan(workspace, plan);
28869
- const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(join38(taskDir(workspace, task), plan.prd.path), "utf8");
28870
- const manifestPath = join38(d2cProductDirectory(workspace, task), "prototype", "interaction-manifest.json");
29013
+ const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(join39(taskDir(workspace, task), plan.prd.path), "utf8");
29014
+ const manifestPath = join39(d2cProductDirectory(workspace, task), "prototype", "interaction-manifest.json");
28871
29015
  const validationError = plan.phase === "design-generating" && await fileTimestamp(manifestPath) !== void 0 ? await validateAndNormalizePrototypeManifest(manifestPath) : void 0;
28872
29016
  return {
28873
29017
  plan,
@@ -29026,7 +29170,7 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
29026
29170
 
29027
29171
  // src/e2e/delivery-run.ts
29028
29172
  import { createHash as createHash26 } from "crypto";
29029
- import { join as join39 } from "path";
29173
+ import { join as join40 } from "path";
29030
29174
  import { z as z45 } from "zod";
29031
29175
  var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
29032
29176
  var ArtifactSchema = z45.object({
@@ -29143,7 +29287,7 @@ function completeDeliveryNode(run, node, outputs, now = /* @__PURE__ */ new Date
29143
29287
  return { ...run, nodes, updatedAt: timestamp2 };
29144
29288
  }
29145
29289
  function deliveryRunPath(workspace, task) {
29146
- return join39(taskDir(workspace, task), "delivery-run.json");
29290
+ return join40(taskDir(workspace, task), "delivery-run.json");
29147
29291
  }
29148
29292
  function decodeDeliveryRun(raw) {
29149
29293
  return DeliveryRunSchema.parse(JSON.parse(raw));
@@ -29186,7 +29330,7 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
29186
29330
  // src/skills/manager.ts
29187
29331
  import { lstat as lstat13, mkdir as mkdir26, readFile as readFile34, readdir as readdir11, realpath as realpath13, rm as rm14, writeFile as writeFile16 } from "fs/promises";
29188
29332
  import { homedir as homedir3 } from "os";
29189
- import { isAbsolute as isAbsolute16, join as join40, relative as relative24, resolve as resolve34, sep as sep25 } from "path";
29333
+ import { isAbsolute as isAbsolute16, join as join41, relative as relative24, resolve as resolve34, sep as sep25 } from "path";
29190
29334
  import { stringify } from "yaml";
29191
29335
  var SkillManager = class {
29192
29336
  #workspace;
@@ -29209,7 +29353,7 @@ var SkillManager = class {
29209
29353
  async create(draft) {
29210
29354
  const value = parseDraft(draft);
29211
29355
  const projectRoot = this.#projectRoot();
29212
- const directory = join40(projectRoot, value.name);
29356
+ const directory = join41(projectRoot, value.name);
29213
29357
  await mkdir26(projectRoot, { recursive: true, mode: 448 });
29214
29358
  try {
29215
29359
  await mkdir26(directory, { mode: 448 });
@@ -29218,7 +29362,7 @@ var SkillManager = class {
29218
29362
  throw error;
29219
29363
  }
29220
29364
  try {
29221
- await writeFile16(join40(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
29365
+ await writeFile16(join41(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
29222
29366
  await this.#persistEnabled(value.name, true);
29223
29367
  return this.get(value.name);
29224
29368
  } catch (error) {
@@ -29236,7 +29380,7 @@ var SkillManager = class {
29236
29380
  throw error;
29237
29381
  });
29238
29382
  if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`Project skill path is not a real directory: ${validName}`);
29239
- const skillFile = join40(directory, "SKILL.md");
29383
+ const skillFile = join41(directory, "SKILL.md");
29240
29384
  const fileInfo = await lstat13(skillFile);
29241
29385
  if (!fileInfo.isFile() || fileInfo.isSymbolicLink()) throw new Error(`Project skill file is not a real file: ${validName}`);
29242
29386
  await writeFile16(skillFile, renderSkill(value), { encoding: "utf8", mode: 384 });
@@ -29268,12 +29412,12 @@ var SkillManager = class {
29268
29412
  async #registry() {
29269
29413
  const loaded = await loadConfig({ cwd: this.#workspace, home: this.#home });
29270
29414
  const [globalPluginRoots, projectPluginRoots] = await Promise.all([
29271
- discoverPluginSkillRoots(join40(this.#home, ".flavor-code", "plugins")),
29272
- discoverPluginSkillRoots(join40(this.#workspace, ".flavor", "plugins"))
29415
+ discoverPluginSkillRoots(join41(this.#home, ".flavor-code", "plugins")),
29416
+ discoverPluginSkillRoots(join41(this.#workspace, ".flavor", "plugins"))
29273
29417
  ]);
29274
29418
  return {
29275
29419
  registry: new SkillRegistry({
29276
- globalRoots: [join40(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
29420
+ globalRoots: [join41(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
29277
29421
  projectRoots: [this.#projectRoot(), ...projectPluginRoots]
29278
29422
  }),
29279
29423
  disabled: new Set(loaded.config.skills.disabled)
@@ -29289,10 +29433,10 @@ var SkillManager = class {
29289
29433
  };
29290
29434
  }
29291
29435
  #projectRoot() {
29292
- return join40(this.#workspace, ".flavor", "skills");
29436
+ return join41(this.#workspace, ".flavor", "skills");
29293
29437
  }
29294
29438
  #projectSkillDirectory(name) {
29295
- return join40(this.#projectRoot(), name);
29439
+ return join41(this.#projectRoot(), name);
29296
29440
  }
29297
29441
  };
29298
29442
  function parseName(value) {
@@ -29333,9 +29477,9 @@ async function discoverPluginSkillRoots(directory) {
29333
29477
  const roots = [];
29334
29478
  for (const entry2 of entries) {
29335
29479
  if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue;
29336
- const pluginRoot = await realpath13(join40(directory, entry2.name));
29480
+ const pluginRoot = await realpath13(join41(directory, entry2.name));
29337
29481
  try {
29338
- const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(join40(pluginRoot, "flavor-plugin.json"), "utf8")));
29482
+ const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(join41(pluginRoot, "flavor-plugin.json"), "utf8")));
29339
29483
  for (const contribution of manifest.contributes.skillRoots) {
29340
29484
  const candidate = resolve34(pluginRoot, contribution.path);
29341
29485
  const info = await lstat13(candidate);
@@ -29410,11 +29554,11 @@ async function createProjectMemoryManager(options) {
29410
29554
  }
29411
29555
 
29412
29556
  // src/mcp/config-manager.ts
29413
- import { join as join41 } from "path";
29557
+ import { join as join42 } from "path";
29414
29558
  var ProjectMcpConfigManager = class {
29415
29559
  path;
29416
29560
  constructor(workspace) {
29417
- this.path = join41(workspace, ".flavor", "flavor.json");
29561
+ this.path = join42(workspace, ".flavor", "flavor.json");
29418
29562
  }
29419
29563
  async list() {
29420
29564
  const document = await readRecoverableFile(this.path, (raw) => parseDocument2(this.path, raw));
@@ -29511,7 +29655,7 @@ function isRecord7(value) {
29511
29655
  }
29512
29656
 
29513
29657
  // src/desktop/model-config.ts
29514
- import { join as join42 } from "path";
29658
+ import { join as join43 } from "path";
29515
29659
  var DEFAULT_DESKTOP_MODELS = [
29516
29660
  {
29517
29661
  id: "anthropic:deepseek-v4-pro",
@@ -29532,7 +29676,7 @@ var DEFAULT_DESKTOP_MODELS = [
29532
29676
  ];
29533
29677
  async function loadDesktopModels(workspace, home) {
29534
29678
  try {
29535
- const tokens2 = await createFileTokenStore(join42(home, ".flavor-code", "auth.json")).load();
29679
+ const tokens2 = await createFileTokenStore(join43(home, ".flavor-code", "auth.json")).load();
29536
29680
  const pkceModels = [];
29537
29681
  const knownPkce = /* @__PURE__ */ new Set();
29538
29682
  for (const token of Object.values(tokens2)) {
@@ -29677,33 +29821,33 @@ function pngDataUrl(png) {
29677
29821
  }
29678
29822
  async function detectD2cFramework(workspace, task) {
29679
29823
  try {
29680
- const pkg = JSON.parse(await readFile35(join43(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
29824
+ const pkg = JSON.parse(await readFile35(join44(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
29681
29825
  return pkg.dependencies?.react === void 0 ? "vue" : "react";
29682
29826
  } catch {
29683
29827
  return "vue";
29684
29828
  }
29685
29829
  }
29686
29830
  function integrationDirectory(workspace, task) {
29687
- return join43(workspace, ".flavor", "d2c", task, "integration");
29831
+ return join44(workspace, ".flavor", "d2c", task, "integration");
29688
29832
  }
29689
29833
  var D2C_BACKEND_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".cfg", ".env", ".ini", ".js", ".json", ".lock", ".mjs", ".py", ".toml", ".txt", ".yaml", ".yml"]);
29690
29834
  var D2C_BACKEND_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".venv", "__pycache__", "data", "node_modules", "venv"]);
29691
29835
  async function d2cBackendSourceFingerprint(project) {
29692
- const runtimeManifest = join43(project, "server", "flavor-runtime.json");
29836
+ const runtimeManifest = join44(project, "server", "flavor-runtime.json");
29693
29837
  const realBackend = (await stat15(runtimeManifest).catch(() => void 0))?.isFile() === true;
29694
- const root = realBackend ? join43(project, "server") : join43(project, "mock");
29838
+ const root = realBackend ? join44(project, "server") : join44(project, "mock");
29695
29839
  if ((await stat15(root).catch(() => void 0))?.isDirectory() !== true) return void 0;
29696
29840
  const files = [];
29697
29841
  const visit = async (directory) => {
29698
29842
  const entries = await readdir12(directory, { withFileTypes: true });
29699
29843
  for (const entry2 of entries) {
29700
29844
  if (entry2.isDirectory()) {
29701
- if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(join43(directory, entry2.name));
29845
+ if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(join44(directory, entry2.name));
29702
29846
  continue;
29703
29847
  }
29704
29848
  if (!entry2.isFile()) continue;
29705
29849
  const extension2 = entry2.name.includes(".") ? `.${entry2.name.split(".").pop().toLowerCase()}` : "";
29706
- if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join43(directory, entry2.name));
29850
+ if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join44(directory, entry2.name));
29707
29851
  }
29708
29852
  };
29709
29853
  await visit(root);
@@ -30257,7 +30401,7 @@ var DesktopRuntimeController = class {
30257
30401
  if (workflow === void 0 || !reviewProgress(workflow).complete || workflow.stage === "visual-review") {
30258
30402
  throw new Error("Complete visual review before importing OpenAPI");
30259
30403
  }
30260
- const moduleManifest = join43(d2cOutputDirectory(workspace, task), "d2c.modules.json");
30404
+ const moduleManifest = join44(d2cOutputDirectory(workspace, task), "d2c.modules.json");
30261
30405
  const moduleManifestInfo = await stat15(moduleManifest).catch(() => void 0);
30262
30406
  if (moduleManifestInfo === void 0 || !moduleManifestInfo.isFile()) {
30263
30407
  throw new Error("D2C implementation must write d2c.modules.json before API mapping; ask the agent to split the project into modules first");
@@ -30270,8 +30414,8 @@ var DesktopRuntimeController = class {
30270
30414
  await mkdir27(dir, { recursive: true });
30271
30415
  const hash = createHash27("sha256").update(raw).digest("hex");
30272
30416
  await Promise.all([
30273
- writeFile17(join43(dir, "swagger.json"), raw),
30274
- writeFile17(join43(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
30417
+ writeFile17(join44(dir, "swagger.json"), raw),
30418
+ writeFile17(join44(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
30275
30419
  `)
30276
30420
  ]);
30277
30421
  const next = {
@@ -30306,7 +30450,7 @@ var DesktopRuntimeController = class {
30306
30450
  let workflow = await readWorkflow(workspace, task);
30307
30451
  if (workflow?.openapi === void 0 && workflow !== void 0 && reviewProgress(workflow).complete && await readD2cProductPlanView(workspace, task) !== void 0) {
30308
30452
  const modules = await readD2cModules(workspace, task);
30309
- const productOpenApiPath = join43(taskDir(workspace, task), "product", "openapi.json");
30453
+ const productOpenApiPath = join44(taskDir(workspace, task), "product", "openapi.json");
30310
30454
  const authored = await readFile35(productOpenApiPath, "utf8").catch(() => void 0);
30311
30455
  const generated = authored === void 0;
30312
30456
  const prepared = await this.#storeD2cOpenApi(
@@ -30319,7 +30463,7 @@ var DesktopRuntimeController = class {
30319
30463
  return prepared;
30320
30464
  }
30321
30465
  if (workflow?.openapi === void 0 || workflow.mappings === void 0) return void 0;
30322
- const document = JSON.parse(await readFile35(join43(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
30466
+ const document = JSON.parse(await readFile35(join44(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
30323
30467
  return { workflow, document, mappings: workflow.mappings };
30324
30468
  }
30325
30469
  async confirmD2cMapping(task, moduleId, operationKey) {
@@ -30386,7 +30530,7 @@ var DesktopRuntimeController = class {
30386
30530
  ].join("\n");
30387
30531
  const delivery = await readDeliveryRun(workspace, task);
30388
30532
  if (delivery?.nodes.api.status === "running") {
30389
- const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile35(join43(project, file)))));
30533
+ const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile35(join44(project, file)))));
30390
30534
  const completed = await updateDeliveryRun(
30391
30535
  workspace,
30392
30536
  task,
@@ -30413,7 +30557,7 @@ var DesktopRuntimeController = class {
30413
30557
  try {
30414
30558
  const fingerprint2 = await d2cBackendSourceFingerprint(project);
30415
30559
  if (fingerprint2 !== void 0) this.#d2cMockSourceFingerprints.set(task, fingerprint2);
30416
- const environmentPath = join43(project, ".env.local");
30560
+ const environmentPath = join44(project, ".env.local");
30417
30561
  const existingEnvironment = await readFile35(environmentPath, "utf8").catch(() => "");
30418
30562
  const environment = `${existingEnvironment.split(/\r?\n/).filter((line) => !/^\s*VITE_API_BASE_URL\s*=/.test(line)).join("\n").trim()}
30419
30563
  VITE_API_BASE_URL=${running.url}
@@ -30494,7 +30638,7 @@ VITE_API_BASE_URL=${running.url}
30494
30638
  const readyPreview = this.#d2cPreviews.get(task);
30495
30639
  const readyMock = this.#d2cMocks.get(task);
30496
30640
  if (readyPreview === void 0 || readyMock === void 0) throw new Error("D2C preview or backend service is unavailable after synchronization");
30497
- const manifestPath = join43(taskDir(workspace, task), "design", "interaction-manifest.json");
30641
+ const manifestPath = join44(taskDir(workspace, task), "design", "interaction-manifest.json");
30498
30642
  const manifest = parseInteractionManifest(await readFile35(manifestPath, "utf8"));
30499
30643
  const baseline = await this.#verifyAcceptanceBaseline(workspace, task);
30500
30644
  const coverage = baseline === void 0 ? void 0 : verifyRequirementCoverage(baseline.approvedPrd.criteria, manifest);
@@ -30506,7 +30650,7 @@ VITE_API_BASE_URL=${running.url}
30506
30650
  await mkdir27(planDir, { recursive: true });
30507
30651
  try {
30508
30652
  const observations = await this.#observeD2cPages(manifest, readyPreview.url);
30509
- const prd = await readFile35(join43(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
30653
+ const prd = await readFile35(join44(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
30510
30654
  const apiContext = JSON.stringify({ openapi: workflow.openapi, mappings: workflow.mappings }, null, 2);
30511
30655
  const plan = await this.#d2cJudge.planInteractions({ task, seed: manifest, observations, ...prd === void 0 ? {} : { prd }, apiContext });
30512
30656
  executableManifest = mergeInteractionManifests(manifest, plan.manifest);
@@ -30517,9 +30661,9 @@ VITE_API_BASE_URL=${running.url}
30517
30661
  plannedScenarios: executableManifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
30518
30662
  pageAnalyses: plan.pageAnalyses
30519
30663
  };
30520
- await writeFile17(join43(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
30664
+ await writeFile17(join44(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
30521
30665
  `, { encoding: "utf8", mode: 384 });
30522
- await rm15(join43(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
30666
+ await rm15(join44(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
30523
30667
  } catch (cause) {
30524
30668
  const detail = message(cause);
30525
30669
  review = {
@@ -30527,8 +30671,8 @@ VITE_API_BASE_URL=${running.url}
30527
30671
  plannedScenarios: manifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
30528
30672
  warning: `\u591A\u6A21\u6001\u81EA\u4E3B\u89C4\u5212\u5931\u8D25\uFF0C\u5DF2\u7EE7\u7EED\u6267\u884C\u5DF2\u786E\u8BA4\u7684\u8BBE\u8BA1\u5951\u7EA6\uFF1A${detail}`
30529
30673
  };
30530
- await rm15(join43(planDir, "autonomous-interaction-plan.json"), { force: true });
30531
- await writeFile17(join43(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
30674
+ await rm15(join44(planDir, "autonomous-interaction-plan.json"), { force: true });
30675
+ await writeFile17(join44(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
30532
30676
  schema: 1,
30533
30677
  stage: "planning",
30534
30678
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -30542,13 +30686,13 @@ VITE_API_BASE_URL=${running.url}
30542
30686
  let result = { ...rawResult, evidenceMode: review.mode };
30543
30687
  if (coverage !== void 0) {
30544
30688
  const evidence = createAcceptanceEvidence(coverage, result);
30545
- await writeFile17(join43(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
30689
+ await writeFile17(join44(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
30546
30690
  `, { encoding: "utf8", mode: 384 });
30547
30691
  if (!evidence.passed) result = { ...result, passed: false, failures: Math.max(1, result.failures) };
30548
30692
  }
30549
30693
  const dir = integrationDirectory(workspace, task);
30550
30694
  await mkdir27(dir, { recursive: true });
30551
- await writeFile17(join43(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
30695
+ await writeFile17(join44(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
30552
30696
  `);
30553
30697
  const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyInteractionRun(current, result));
30554
30698
  return { workflow: next, result, review };
@@ -30561,7 +30705,7 @@ VITE_API_BASE_URL=${running.url}
30561
30705
  if (mock === void 0 || !mock.exited() || result.failures === 0) return result;
30562
30706
  const output = mock.output().trim();
30563
30707
  try {
30564
- const dir = join43(d2cOutputDirectory(workspace, task), "mock-server.log");
30708
+ const dir = join44(d2cOutputDirectory(workspace, task), "mock-server.log");
30565
30709
  await writeFile17(dir, `${output}
30566
30710
  `);
30567
30711
  } catch {
@@ -30656,7 +30800,7 @@ VITE_API_BASE_URL=${running.url}
30656
30800
  if (current === void 0) throw new Error("D2C product plan is unavailable");
30657
30801
  if (current.plan.approvedPrd !== void 0) throw new Error("The approved PRD is immutable and cannot be edited");
30658
30802
  if (current.plan.phase !== "prd-review") throw new Error("PRD sections can only be edited during PRD review");
30659
- const path = join43(taskDir(workspace, task), "product", "prd.md");
30803
+ const path = join44(taskDir(workspace, task), "product", "prd.md");
30660
30804
  const updated = await updatePrdSectionFile(path, { sectionId, body, expectedHash });
30661
30805
  const now = (/* @__PURE__ */ new Date()).toISOString();
30662
30806
  await writeD2cProductPlan(workspace, {
@@ -30676,15 +30820,15 @@ VITE_API_BASE_URL=${running.url}
30676
30820
  async #assertProductPrdLocked(workspace, task) {
30677
30821
  const current = await readD2cProductPlanView(workspace, task);
30678
30822
  if (current?.plan.approvedPrd === void 0) return;
30679
- await assertApprovedPrd(join43(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
30823
+ await assertApprovedPrd(join44(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
30680
30824
  }
30681
30825
  async #acceptancePaths(workspace, task) {
30682
- const product = join43(taskDir(workspace, task), "product");
30683
- const openapi = join43(product, "openapi.json");
30826
+ const product = join44(taskDir(workspace, task), "product");
30827
+ const openapi = join44(product, "openapi.json");
30684
30828
  return {
30685
- prd: join43(product, "prd.md"),
30686
- prototype: join43(product, "prototype", "index.html"),
30687
- interaction: join43(product, "prototype", "interaction-manifest.json"),
30829
+ prd: join44(product, "prd.md"),
30830
+ prototype: join44(product, "prototype", "index.html"),
30831
+ interaction: join44(product, "prototype", "interaction-manifest.json"),
30688
30832
  ...await stat15(openapi).catch(() => void 0) === void 0 ? {} : { openapi }
30689
30833
  };
30690
30834
  }
@@ -30692,17 +30836,17 @@ VITE_API_BASE_URL=${running.url}
30692
30836
  const product = await readD2cProductPlanView(workspace, task);
30693
30837
  if (product?.plan.approvedPrd === void 0) return void 0;
30694
30838
  await this.#assertProductPrdLocked(workspace, task);
30695
- const baseline = JSON.parse(await readFile35(join43(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
30839
+ const baseline = JSON.parse(await readFile35(join44(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
30696
30840
  await verifyAcceptanceBaseline(await this.#acceptancePaths(workspace, task), baseline);
30697
- const design = join43(taskDir(workspace, task), "design");
30841
+ const design = join44(taskDir(workspace, task), "design");
30698
30842
  await Promise.all([
30699
- verifyAcceptanceArtifact(join43(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
30700
- verifyAcceptanceArtifact(join43(design, "interaction-manifest.json"), baseline.artifacts.interaction, "runtime interaction contract")
30843
+ verifyAcceptanceArtifact(join44(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
30844
+ verifyAcceptanceArtifact(join44(design, "interaction-manifest.json"), baseline.artifacts.interaction, "runtime interaction contract")
30701
30845
  ]);
30702
30846
  const workflow = await readWorkflow(workspace, task);
30703
30847
  if (workflow?.openapi !== void 0) {
30704
30848
  await verifyAcceptanceArtifact(
30705
- join43(taskDir(workspace, task), "integration", "swagger.json"),
30849
+ join44(taskDir(workspace, task), "integration", "swagger.json"),
30706
30850
  { hash: workflow.openapi.hash },
30707
30851
  "runtime OpenAPI contract"
30708
30852
  );
@@ -30723,14 +30867,14 @@ VITE_API_BASE_URL=${running.url}
30723
30867
  throw new Error("D2C PRD is empty and cannot be accepted");
30724
30868
  }
30725
30869
  if (stage === "design" && accepted) {
30726
- const manifest = join43(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
30870
+ const manifest = join44(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
30727
30871
  const normalizedManifest = parseInteractionManifest(await readFile35(manifest, "utf8"));
30728
30872
  if (current.plan.approvedPrd === void 0) throw new Error("Approve the PRD before confirming the design");
30729
30873
  verifyRequirementCoverage(current.plan.approvedPrd.criteria, normalizedManifest);
30730
30874
  await writeFile17(manifest, `${JSON.stringify(normalizedManifest, null, 2)}
30731
30875
  `, { encoding: "utf8", mode: 384 });
30732
30876
  const baseline = await captureAcceptanceBaseline(await this.#acceptancePaths(workspace, task), current.plan.approvedPrd);
30733
- await writeFile17(join43(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
30877
+ await writeFile17(join44(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
30734
30878
  `, { encoding: "utf8", mode: 384 });
30735
30879
  }
30736
30880
  const plan = await writeD2cProductPlan(
@@ -30764,8 +30908,8 @@ VITE_API_BASE_URL=${running.url}
30764
30908
  const imported = await this.importD2cDesign(task, d2cProductPrototypeDirectory(workspace, task));
30765
30909
  const delivery = await readDeliveryRun(workspace, task);
30766
30910
  if (delivery !== void 0) {
30767
- const prototype = await readFile35(join43(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
30768
- const interaction = await readFile35(join43(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json"), "utf8");
30911
+ const prototype = await readFile35(join44(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
30912
+ const interaction = await readFile35(join44(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json"), "utf8");
30769
30913
  const completed = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "design", [
30770
30914
  artifactRef("product/prototype/index.html", prototype),
30771
30915
  artifactRef("product/prototype/interaction-manifest.json", interaction)
@@ -30849,10 +30993,10 @@ VITE_API_BASE_URL=${running.url}
30849
30993
  const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyQualityJudgment(current, judgment));
30850
30994
  const qualityContent = `${JSON.stringify(judgment, null, 2)}
30851
30995
  `;
30852
- await writeFile17(join43(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
30996
+ await writeFile17(join44(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
30853
30997
  const delivery = await readDeliveryRun(workspace, task);
30854
30998
  if (next.stage === "completed" && delivery?.nodes.acceptance.status === "running") {
30855
- const interactionContent = await readFile35(join43(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
30999
+ const interactionContent = await readFile35(join44(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
30856
31000
  const accepted = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "acceptance", [
30857
31001
  artifactRef("integration/interaction-results.json", interactionContent),
30858
31002
  artifactRef("quality-judge.json", qualityContent)
@@ -31033,15 +31177,15 @@ function desktopWindowChrome(platform = process.platform) {
31033
31177
  }
31034
31178
 
31035
31179
  // src/utils/crash-guard.ts
31036
- import { mkdirSync as mkdirSync3, writeFileSync } from "fs";
31037
- import { join as join44 } from "path";
31180
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
31181
+ import { join as join45 } from "path";
31038
31182
  var installed = false;
31039
31183
  function installCrashGuard(options = {}) {
31040
31184
  if (installed) return;
31041
31185
  installed = true;
31042
31186
  const crashLogDir = () => {
31043
31187
  try {
31044
- return join44((options.workspace ?? process.cwd)(), ".flavor");
31188
+ return join45((options.workspace ?? process.cwd)(), ".flavor");
31045
31189
  } catch {
31046
31190
  return process.cwd();
31047
31191
  }
@@ -31049,9 +31193,9 @@ function installCrashGuard(options = {}) {
31049
31193
  const writeCrashLog = (kind, detail) => {
31050
31194
  try {
31051
31195
  const dir = crashLogDir();
31052
- mkdirSync3(dir, { recursive: true });
31053
- const path = join44(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
31054
- writeFileSync(path, [
31196
+ mkdirSync4(dir, { recursive: true });
31197
+ const path = join45(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
31198
+ writeFileSync2(path, [
31055
31199
  `kind: ${kind}`,
31056
31200
  `version: ${process.version}`,
31057
31201
  `platform: ${process.platform}`,
@@ -31243,7 +31387,7 @@ function safeGitTarget(value) {
31243
31387
  // src/desktop/worktree-manager.ts
31244
31388
  import { randomUUID as randomUUID28 } from "crypto";
31245
31389
  import { mkdir as mkdir28, readFile as readFile37, readdir as readdir13, rm as rm17, stat as stat16, writeFile as writeFile18 } from "fs/promises";
31246
- import { basename as basename10, isAbsolute as isAbsolute19, join as join45, relative as relative27, resolve as resolve38, sep as sep27 } from "path";
31390
+ import { basename as basename10, isAbsolute as isAbsolute19, join as join46, relative as relative27, resolve as resolve38, sep as sep27 } from "path";
31247
31391
  var DesktopWorktreeManager = class {
31248
31392
  #repository;
31249
31393
  #storage;
@@ -31289,7 +31433,7 @@ var DesktopWorktreeManager = class {
31289
31433
  }
31290
31434
  const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
31291
31435
  try {
31292
- const stored = validateStored(JSON.parse(await readFile37(join45(this.#storage, name), "utf8")), this.#repository, this.#storage);
31436
+ const stored = validateStored(JSON.parse(await readFile37(join46(this.#storage, name), "utf8")), this.#repository, this.#storage);
31293
31437
  if ((await stat16(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
31294
31438
  const status = await git2(stored.path, ["status", "--porcelain"]);
31295
31439
  const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
@@ -31353,7 +31497,7 @@ function validateStored(value, repository, storage) {
31353
31497
  // src/desktop/workbench-service.ts
31354
31498
  import { readFile as readFile38, readdir as readdir14, stat as stat17 } from "fs/promises";
31355
31499
  import { homedir as homedir5 } from "os";
31356
- import { join as join46, resolve as resolve39 } from "path";
31500
+ import { join as join47, resolve as resolve39 } from "path";
31357
31501
  var MAX_TEXT = 128e3;
31358
31502
  var MAX_ITEMS = 200;
31359
31503
  var DesktopWorkbenchService = class {
@@ -31386,11 +31530,11 @@ var DesktopWorkbenchService = class {
31386
31530
  return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
31387
31531
  }
31388
31532
  async #goals() {
31389
- const root = join46(this.#workspace, ".flavor", "goals");
31533
+ const root = join47(this.#workspace, ".flavor", "goals");
31390
31534
  const names = await readdir14(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
31391
31535
  const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
31392
31536
  try {
31393
- return GoalStateSchema.parse(JSON.parse(await boundedRead2(join46(root, name))));
31537
+ return GoalStateSchema.parse(JSON.parse(await boundedRead2(join47(root, name))));
31394
31538
  } catch {
31395
31539
  return void 0;
31396
31540
  }
@@ -31400,7 +31544,7 @@ var DesktopWorkbenchService = class {
31400
31544
  async #instructions() {
31401
31545
  const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
31402
31546
  const items = await Promise.all(names.map(async (name) => {
31403
- const path = join46(this.#workspace, name);
31547
+ const path = join47(this.#workspace, name);
31404
31548
  try {
31405
31549
  return { name, path, content: redactErrorText(await boundedRead2(path)) };
31406
31550
  } catch (error) {
@@ -31413,9 +31557,9 @@ var DesktopWorkbenchService = class {
31413
31557
  async #permissionFiles() {
31414
31558
  const candidates = [
31415
31559
  ...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve39(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
31416
- { tier: "user", path: join46(this.#home, ".flavor-code", "permissions.json"), content: "" },
31417
- { tier: "project", path: join46(this.#workspace, ".flavor", "permissions.json"), content: "" },
31418
- { tier: "local", path: join46(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
31560
+ { tier: "user", path: join47(this.#home, ".flavor-code", "permissions.json"), content: "" },
31561
+ { tier: "project", path: join47(this.#workspace, ".flavor", "permissions.json"), content: "" },
31562
+ { tier: "local", path: join47(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
31419
31563
  ];
31420
31564
  const values = await Promise.all(candidates.map(async (item) => {
31421
31565
  try {
@@ -31430,7 +31574,7 @@ var DesktopWorkbenchService = class {
31430
31574
  async #audit() {
31431
31575
  let raw;
31432
31576
  try {
31433
- raw = await boundedRead2(join46(this.#workspace, ".flavor", "audit.jsonl"));
31577
+ raw = await boundedRead2(join47(this.#workspace, ".flavor", "audit.jsonl"));
31434
31578
  } catch (error) {
31435
31579
  if (error.code === "ENOENT") return [];
31436
31580
  throw error;
@@ -31445,7 +31589,7 @@ var DesktopWorkbenchService = class {
31445
31589
  }).reverse();
31446
31590
  }
31447
31591
  async #context(sessionId) {
31448
- const usage = await readJsonLines2(join46(this.#workspace, ".flavor", "usage.jsonl"));
31592
+ const usage = await readJsonLines2(join47(this.#workspace, ".flavor", "usage.jsonl"));
31449
31593
  if (sessionId === void 0) return { visibility: [], usage };
31450
31594
  try {
31451
31595
  const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
@@ -31485,14 +31629,14 @@ function redact2(value, key = "") {
31485
31629
 
31486
31630
  // src/desktop/main.ts
31487
31631
  installCrashGuard();
31488
- var moduleDirectory = dirname24(fileURLToPath4(import.meta.url));
31632
+ var moduleDirectory = dirname25(fileURLToPath4(import.meta.url));
31489
31633
  var developmentUrl = process.env.FLAVOR_DESKTOP_DEV_URL;
31490
31634
  var mainWindow;
31491
31635
  var appMenu;
31492
31636
  var quitting = false;
31493
31637
  function logStartup(step, detail) {
31494
31638
  try {
31495
- const logPath = join47(dirname24(process.execPath), "flavor-code-startup.log");
31639
+ const logPath = join48(dirname25(process.execPath), "flavor-code-startup.log");
31496
31640
  const ts = (/* @__PURE__ */ new Date()).toISOString();
31497
31641
  appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
31498
31642
  `);
@@ -31517,7 +31661,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
31517
31661
  };
31518
31662
  });
31519
31663
  var d2cJudgeClient = createD2cJudgeClient();
31520
- var judgeStore = () => createD2cJudgeConfigStore(join47(app.getPath("userData"), "d2c-judge.json"));
31664
+ var judgeStore = () => createD2cJudgeConfigStore(join48(app.getPath("userData"), "d2c-judge.json"));
31521
31665
  function judgeImage(png) {
31522
31666
  const image = nativeImage.createFromBuffer(png);
31523
31667
  const size = image.getSize();
@@ -31543,7 +31687,7 @@ function sessionMetaKey(workspace, sessionId) {
31543
31687
  }
31544
31688
  function worktreeManager(workspace) {
31545
31689
  const key = createHash28("sha256").update(resolve40(workspace)).digest("hex").slice(0, 20);
31546
- return new DesktopWorktreeManager({ repository: workspace, storage: join47(app.getPath("userData"), "task-worktrees", key) });
31690
+ return new DesktopWorktreeManager({ repository: workspace, storage: join48(app.getPath("userData"), "task-worktrees", key) });
31547
31691
  }
31548
31692
  function activeWorkingDirectory() {
31549
31693
  if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
@@ -31688,7 +31832,7 @@ function createDesktopController(workspace, owner2) {
31688
31832
  alias: `desktop-${basename11(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
31689
31833
  optional: true,
31690
31834
  startBroker: (address) => {
31691
- const entry2 = app.isPackaged ? join47(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join47(moduleDirectory, "pals-broker-entry.js");
31835
+ const entry2 = app.isPackaged ? join48(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join48(moduleDirectory, "pals-broker-entry.js");
31692
31836
  return spawnPalBrokerProcess({
31693
31837
  address,
31694
31838
  executable: process.execPath,
@@ -31710,7 +31854,7 @@ function createDesktopController(workspace, owner2) {
31710
31854
  var detachedController = createDesktopController();
31711
31855
  var controller = detachedController;
31712
31856
  function statePath() {
31713
- return join47(app.getPath("userData"), "desktop-state.json");
31857
+ return join48(app.getPath("userData"), "desktop-state.json");
31714
31858
  }
31715
31859
  async function loadPersistedProjects() {
31716
31860
  try {
@@ -31883,7 +32027,7 @@ async function chooseAndOpenWorkspace() {
31883
32027
  var cachedAppIconDataUrl;
31884
32028
  function appIconDataUrl() {
31885
32029
  if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
31886
- const icon = nativeImage.createFromPath(join47(app.getAppPath(), "assets", "icon.png"));
32030
+ const icon = nativeImage.createFromPath(join48(app.getAppPath(), "assets", "icon.png"));
31887
32031
  if (icon.isEmpty()) return void 0;
31888
32032
  cachedAppIconDataUrl = icon.toDataURL();
31889
32033
  return cachedAppIconDataUrl;
@@ -32301,7 +32445,7 @@ function applicationMenu() {
32301
32445
  ]);
32302
32446
  }
32303
32447
  async function createWindow() {
32304
- const rendererPath = join47(app.getAppPath(), "dist", "desktop-renderer", "index.html");
32448
+ const rendererPath = join48(app.getAppPath(), "dist", "desktop-renderer", "index.html");
32305
32449
  mainWindow = new BrowserWindow2({
32306
32450
  title: "Flavor Code",
32307
32451
  width: 1280,
@@ -32312,14 +32456,14 @@ async function createWindow() {
32312
32456
  show: false,
32313
32457
  ...desktopWindowChrome(),
32314
32458
  webPreferences: {
32315
- preload: join47(moduleDirectory, "preload.cjs"),
32459
+ preload: join48(moduleDirectory, "preload.cjs"),
32316
32460
  contextIsolation: true,
32317
32461
  nodeIntegration: false,
32318
32462
  sandbox: false,
32319
32463
  webSecurity: true
32320
32464
  }
32321
32465
  });
32322
- const iconPath = join47(app.getAppPath(), "assets", "icon.png");
32466
+ const iconPath = join48(app.getAppPath(), "assets", "icon.png");
32323
32467
  const icon = nativeImage.createFromPath(iconPath);
32324
32468
  if (!icon.isEmpty()) {
32325
32469
  mainWindow.setIcon(icon);