flavor-code 1.3.13 → 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];
@@ -24993,9 +25112,28 @@ async function createProductionRuntime(options) {
24993
25112
  const recovered = options.resumeSession === void 0 ? void 0 : await sessionStore.load(options.resumeSession === true ? void 0 : options.resumeSession);
24994
25113
  let timelineState = recovered === void 0 ? createTranscriptState() : restoreTranscriptState(recovered.timeline.state);
24995
25114
  const restoredTranscript = restoreTranscriptState(timelineState);
25115
+ const pendingTimelineStreams = [];
25116
+ const flushTimelineStreams = () => {
25117
+ for (const pending2 of pendingTimelineStreams.splice(0)) {
25118
+ timelineState = transcriptReducer(timelineState, {
25119
+ type: "session",
25120
+ event: { type: pending2.type, text: pending2.chunks.join("") }
25121
+ });
25122
+ }
25123
+ };
25124
+ const recordTimelineEvent = (event) => {
25125
+ if (event.type === "text" || event.type === "thinking") {
25126
+ const last = pendingTimelineStreams.at(-1);
25127
+ if (last?.type === event.type) last.chunks.push(event.text);
25128
+ else pendingTimelineStreams.push({ type: event.type, chunks: [event.text] });
25129
+ return;
25130
+ }
25131
+ flushTimelineStreams();
25132
+ timelineState = transcriptReducer(timelineState, { type: "session", event });
25133
+ };
24996
25134
  let ideSessionId;
24997
25135
  const emitOutput = (event) => {
24998
- timelineState = transcriptReducer(timelineState, { type: "session", event });
25136
+ recordTimelineEvent(event);
24999
25137
  options.output(event);
25000
25138
  if (ideSessionId !== void 0) ide.publishEvent(ideSessionId, event);
25001
25139
  };
@@ -25132,8 +25270,8 @@ async function createProductionRuntime(options) {
25132
25270
  const registeredProviders = registration.registered;
25133
25271
  let effectiveLlm = registration.effectiveLlm;
25134
25272
  const pluginHost = new PluginHost({
25135
- globalPluginDirs: [join33(home, ".flavor-code", "plugins")],
25136
- projectPluginDirs: [join33(workspace, ".flavor", "plugins")],
25273
+ globalPluginDirs: [join34(home, ".flavor-code", "plugins")],
25274
+ projectPluginDirs: [join34(workspace, ".flavor", "plugins")],
25137
25275
  config,
25138
25276
  // Existing project plugins (including astgraph and superharness) use Node.js
25139
25277
  // built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
@@ -25241,7 +25379,7 @@ async function createProductionRuntime(options) {
25241
25379
  const address = palSocketAddress({
25242
25380
  platform: process.platform,
25243
25381
  userScope: home,
25244
- 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)}`)
25245
25383
  });
25246
25384
  const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
25247
25385
  instanceId: collaboration.instanceId,
@@ -25352,8 +25490,8 @@ async function createProductionRuntime(options) {
25352
25490
  diagnostics.push(`MCP servers could not start: ${message(error)}`);
25353
25491
  });
25354
25492
  const skills = new SkillRegistry({
25355
- globalRoots: [join33(home, ".flavor-code", "skills")],
25356
- projectRoots: [join33(workspace, ".flavor", "skills"), ...pluginSkillRoots],
25493
+ globalRoots: [join34(home, ".flavor-code", "skills")],
25494
+ projectRoots: [join34(workspace, ".flavor", "skills"), ...pluginSkillRoots],
25357
25495
  authorizeResource: async () => true,
25358
25496
  disabledNames: config.skills.disabled
25359
25497
  });
@@ -25361,7 +25499,7 @@ async function createProductionRuntime(options) {
25361
25499
  skillsReady.catch(() => void 0);
25362
25500
  tools.push(createSkillTool(skills), createSkillResourceTool(skills));
25363
25501
  if (options.extraTools !== void 0) tools.push(...options.extraTools);
25364
- const flavor = await optionalText2(join33(workspace, "FLAVOR.md"));
25502
+ const flavor = await optionalText2(join34(workspace, "FLAVOR.md"));
25365
25503
  const instructionBaseline = await workspaceInstructions.baseline();
25366
25504
  let memoryContext;
25367
25505
  if (memoryStore !== void 0) {
@@ -25412,27 +25550,30 @@ async function createProductionRuntime(options) {
25412
25550
  };
25413
25551
  }
25414
25552
  let persistTail = Promise.resolve();
25415
- const sessionDocument = () => ({
25416
- version: SESSION_VERSION,
25417
- sessionId,
25418
- createdAt,
25419
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
25420
- workspace: { path: workspace },
25421
- conversation: storedConversation(harness.main.context.snapshot()),
25422
- tasks: {
25423
- ...taskPlan === void 0 ? {} : { plan: taskPlan },
25424
- ...taskGraph === void 0 ? {} : { graph: taskGraph },
25425
- states: { ...taskStates },
25426
- results: { ...taskResults }
25427
- },
25428
- // Persist the resolved decision, not the harness snapshot: after logout the
25429
- // harness may still hold the old login model, and resuming from that stale
25430
- // value is what made the welcome card show a previous service.
25431
- models: { main: mainModel, subagent: childModel },
25432
- permissionMode: harness.permissionMode,
25433
- memory: memoryLifecycle,
25434
- timeline: { version: 1, state: timelineState }
25435
- });
25553
+ const sessionDocument = () => {
25554
+ flushTimelineStreams();
25555
+ return {
25556
+ version: SESSION_VERSION,
25557
+ sessionId,
25558
+ createdAt,
25559
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
25560
+ workspace: { path: workspace },
25561
+ conversation: storedConversation(harness.main.context.snapshot()),
25562
+ tasks: {
25563
+ ...taskPlan === void 0 ? {} : { plan: taskPlan },
25564
+ ...taskGraph === void 0 ? {} : { graph: taskGraph },
25565
+ states: { ...taskStates },
25566
+ results: { ...taskResults }
25567
+ },
25568
+ // Persist the resolved decision, not the harness snapshot: after logout the
25569
+ // harness may still hold the old login model, and resuming from that stale
25570
+ // value is what made the welcome card show a previous service.
25571
+ models: { main: mainModel, subagent: childModel },
25572
+ permissionMode: harness.permissionMode,
25573
+ memory: memoryLifecycle,
25574
+ timeline: { version: 1, state: timelineState }
25575
+ };
25576
+ };
25436
25577
  let persistFailed = false;
25437
25578
  const persist = () => {
25438
25579
  persistTail = persistTail.catch(() => void 0).then(
@@ -25939,7 +26080,7 @@ async function createProductionRuntime(options) {
25939
26080
  evolveService.toolDefinition(),
25940
26081
  ...mcpTools
25941
26082
  ];
25942
- const loopFlavor = await optionalText2(join33(input.workspace, "FLAVOR.md"));
26083
+ const loopFlavor = await optionalText2(join34(input.workspace, "FLAVOR.md"));
25943
26084
  const loopInstructionBaseline = await loopInstructions.baseline();
25944
26085
  const loopEnvironment = createPromptEnvironment({
25945
26086
  now: /* @__PURE__ */ new Date(),
@@ -26184,7 +26325,7 @@ async function createProductionRuntime(options) {
26184
26325
  contextEpoch: harness.main.context.snapshot().epoch?.id ?? "legacy"
26185
26326
  });
26186
26327
  const turnId = harnessJournal.startTurn(turnConfig, { prompt, initialUserMessage: runOptions?.initialUserMessage });
26187
- return durableTurn(persistAndCheckpointAfter(runMain(
26328
+ return monitorMemoryRestart(durableTurn(persistAndCheckpointAfter(runMain(
26188
26329
  harness,
26189
26330
  skills,
26190
26331
  prompt,
@@ -26204,7 +26345,7 @@ async function createProductionRuntime(options) {
26204
26345
  prompt,
26205
26346
  context: harness.main.context.snapshot(),
26206
26347
  label: `turn: ${prompt.slice(0, 80)}`
26207
- })), harnessJournal, turnId, turnConfig);
26348
+ })), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
26208
26349
  },
26209
26350
  runSkill: (skill, prompt, signal) => persistAfter(
26210
26351
  runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
@@ -26504,7 +26645,7 @@ ${renderMemoryDocument(entries)}`;
26504
26645
  return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
26505
26646
  }
26506
26647
  try {
26507
- const tokenStore = createFileTokenStore(join33(home, ".flavor-code", "auth.json"));
26648
+ const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
26508
26649
  const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
26509
26650
  await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
26510
26651
  const oauth = new OAuthCallbackAuthProvider({
@@ -26551,7 +26692,7 @@ ${renderMemoryDocument(entries)}`;
26551
26692
  }
26552
26693
  },
26553
26694
  async logout() {
26554
- const tokenStore = createFileTokenStore(join33(home, ".flavor-code", "auth.json"));
26695
+ const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
26555
26696
  const tokens2 = await tokenStore.load();
26556
26697
  const hadCredentials = Object.keys(tokens2).length > 0 || effectiveLlm !== void 0;
26557
26698
  if (!hadCredentials) {
@@ -26953,6 +27094,31 @@ async function* durableTurn(source, journal, turnId, config) {
26953
27094
  }
26954
27095
  }
26955
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
+ }
26956
27122
  async function runGitCommit(deps, hint, signal) {
26957
27123
  if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
26958
27124
  let summary2 = await changeSummary(deps.workspace);
@@ -27046,7 +27212,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
27046
27212
  const configured = { ...providers };
27047
27213
  if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
27048
27214
  if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
27049
- const oauthTokenStore = createFileTokenStore(join33(home, ".flavor-code", "auth.json"));
27215
+ const oauthTokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
27050
27216
  const registered = [];
27051
27217
  let effectiveLlm;
27052
27218
  for (const [name, provider] of Object.entries(configured)) {
@@ -27479,11 +27645,11 @@ function createMemoryTaskId() {
27479
27645
  import { createHash as createHash27 } from "crypto";
27480
27646
  import { mkdir as mkdir27, readFile as readFile35, readdir as readdir12, rm as rm15, stat as stat15, writeFile as writeFile17 } from "fs/promises";
27481
27647
  import { homedir as homedir4 } from "os";
27482
- 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";
27483
27649
 
27484
27650
  // src/d2c/workflow.ts
27485
27651
  import { createHash as createHash22 } from "crypto";
27486
- import { join as join34 } from "path";
27652
+ import { join as join35 } from "path";
27487
27653
  import { z as z42 } from "zod";
27488
27654
 
27489
27655
  // src/d2c/workflow-shared.ts
@@ -27629,7 +27795,7 @@ function reviewsFor(report, previous) {
27629
27795
  function implementationDirectory(report) {
27630
27796
  const source = report.implementation.source;
27631
27797
  if (!/^https?:\/\//i.test(source)) return source;
27632
- return join34("src", "d2c-output", report.task);
27798
+ return join35("src", "d2c-output", report.task);
27633
27799
  }
27634
27800
  function createWorkflow(report, framework) {
27635
27801
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -27753,7 +27919,7 @@ function applyQualityIssueDecision(workflow, issueId2, decision) {
27753
27919
  };
27754
27920
  }
27755
27921
  function workflowPath(workspace, task) {
27756
- return join34(taskDir(workspace, task), "workflow.json");
27922
+ return join35(taskDir(workspace, task), "workflow.json");
27757
27923
  }
27758
27924
  async function readWorkflow(workspace, task) {
27759
27925
  return (await readRecoverableFile(workflowPath(workspace, task), decodeWorkflow))?.value;
@@ -28007,7 +28173,7 @@ function confirmApiMapping(mapping, operationKey, operations) {
28007
28173
 
28008
28174
  // src/d2c/modules.ts
28009
28175
  import { readFile as readFile28 } from "fs/promises";
28010
- 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";
28011
28177
  import { z as z43 } from "zod";
28012
28178
  var ModuleSchema = z43.object({
28013
28179
  id: z43.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
@@ -28026,13 +28192,13 @@ function safeSource(path) {
28026
28192
  }
28027
28193
  function d2cOutputDirectory(workspace, task) {
28028
28194
  if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
28029
- return join35(workspace, "src", "d2c-output", task);
28195
+ return join36(workspace, "src", "d2c-output", task);
28030
28196
  }
28031
28197
  async function readD2cModules(workspace, task) {
28032
28198
  const project = d2cOutputDirectory(workspace, task);
28033
28199
  let raw;
28034
28200
  try {
28035
- raw = await readFile28(join35(project, "d2c.modules.json"), "utf8");
28201
+ raw = await readFile28(join36(project, "d2c.modules.json"), "utf8");
28036
28202
  } catch {
28037
28203
  return [{ id: "page", label: task, sourceFiles: ["src/App.vue", "src/App.jsx"], keywords: [task] }];
28038
28204
  }
@@ -28053,7 +28219,7 @@ async function readD2cModules(workspace, task) {
28053
28219
 
28054
28220
  // src/d2c/integration.ts
28055
28221
  import { mkdir as mkdir24, readFile as readFile29, writeFile as writeFile13 } from "fs/promises";
28056
- import { join as join36 } from "path";
28222
+ import { join as join37 } from "path";
28057
28223
  function record2(value) {
28058
28224
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
28059
28225
  }
@@ -28189,7 +28355,7 @@ async def reset_acceptance(request: Request):
28189
28355
  }
28190
28356
  async function generateIntegrationArtifacts(projectDir, document, mappings, options = {}) {
28191
28357
  if (mappings.some((mapping) => mapping.status === "needs-confirmation")) throw new Error("Confirm every uncertain OpenAPI mapping before generating integration code");
28192
- const packagePath = join36(projectDir, "package.json");
28358
+ const packagePath = join37(projectDir, "package.json");
28193
28359
  let pkg;
28194
28360
  try {
28195
28361
  pkg = JSON.parse(await readFile29(packagePath, "utf8"));
@@ -28199,9 +28365,9 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
28199
28365
  const scripts = options.pythonServer ? { ...record2(pkg.scripts) ?? {} } : { ...record2(pkg.scripts) ?? {}, mock: "node mock/server.mjs" };
28200
28366
  const dependencies2 = options.pythonServer ? { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9" } : { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9", express: "^5.2.1" };
28201
28367
  const selected = [...new Set(mappings.map((mapping) => mapping.operationKey))].map((key) => document.operations.find((operation) => operation.id === key)).filter((operation) => operation !== void 0);
28202
- const apiDir = join36(projectDir, "src", "api");
28203
- const mockDir = join36(projectDir, "mock");
28204
- const serverDir = join36(projectDir, "server");
28368
+ const apiDir = join37(projectDir, "src", "api");
28369
+ const mockDir = join37(projectDir, "mock");
28370
+ const serverDir = join37(projectDir, "server");
28205
28371
  await Promise.all([
28206
28372
  mkdir24(apiDir, { recursive: true }),
28207
28373
  ...options.pythonServer ? [] : [mkdir24(mockDir, { recursive: true })],
@@ -28211,23 +28377,23 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
28211
28377
  if (options.pythonServer) files.push("server/main.py", "server/requirements.txt", "server/README.md", "server/flavor-runtime.json");
28212
28378
  else files.push("mock/server.mjs");
28213
28379
  await Promise.all([
28214
- writeFile13(join36(apiDir, "http.js"), `import axios from "axios";
28380
+ writeFile13(join37(apiDir, "http.js"), `import axios from "axios";
28215
28381
 
28216
28382
  export const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || ${JSON.stringify(document.baseUrl ?? "")}, timeout: 15000 });
28217
28383
 
28218
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 }));
28219
28385
  `),
28220
- writeFile13(join36(apiDir, "d2c-api.js"), apiModule(selected)),
28221
- 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)}
28222
28388
  `),
28223
- ...options.pythonServer ? [] : [writeFile13(join36(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
28389
+ ...options.pythonServer ? [] : [writeFile13(join37(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
28224
28390
  writeFile13(packagePath, `${JSON.stringify({ ...pkg, scripts, dependencies: dependencies2 }, null, 2)}
28225
28391
  `),
28226
28392
  ...options.pythonServer ? [
28227
- writeFile13(join36(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
28228
- writeFile13(join36(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
28229
- 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"),
28230
- 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({
28231
28397
  schema: 1,
28232
28398
  kind: "python-fastapi",
28233
28399
  cwd: "server",
@@ -28248,7 +28414,7 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
28248
28414
  import { spawn as spawn8 } from "child_process";
28249
28415
  import { access as access3, readFile as readFile30 } from "fs/promises";
28250
28416
  import { createServer as createServer5 } from "net";
28251
- import { join as join37 } from "path";
28417
+ import { join as join38 } from "path";
28252
28418
  async function exists3(path) {
28253
28419
  try {
28254
28420
  await access3(path);
@@ -28356,7 +28522,7 @@ ${output.trim()}` : ""}`);
28356
28522
  }
28357
28523
  }
28358
28524
  async function runPythonBackend(projectDir, options) {
28359
- const serverDir = join37(projectDir, "server");
28525
+ const serverDir = join38(projectDir, "server");
28360
28526
  if (options.installDependencies !== false) await installPython(serverDir, options.signal);
28361
28527
  const selectedPort = await port();
28362
28528
  const url = `http://127.0.0.1:${selectedPort}`;
@@ -28402,7 +28568,7 @@ ${managed.output().slice(-4e3)}`);
28402
28568
  };
28403
28569
  }
28404
28570
  async function runD2cMockServer(projectDir, options = {}) {
28405
- const runtimePath = join37(projectDir, "server", "flavor-runtime.json");
28571
+ const runtimePath = join38(projectDir, "server", "flavor-runtime.json");
28406
28572
  if (await exists3(runtimePath)) {
28407
28573
  let runtime;
28408
28574
  try {
@@ -28413,9 +28579,9 @@ async function runD2cMockServer(projectDir, options = {}) {
28413
28579
  if (runtime.schema !== 1 || runtime.kind !== "python-fastapi") throw new Error("Unsupported E2E backend runtime manifest");
28414
28580
  return runPythonBackend(projectDir, options);
28415
28581
  }
28416
- const serverPath = join37(projectDir, "mock", "server.mjs");
28582
+ const serverPath = join38(projectDir, "mock", "server.mjs");
28417
28583
  if (!await exists3(serverPath)) throw new Error(`D2C mock server is not generated at ${serverPath}`);
28418
- 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"))) {
28419
28585
  throw new Error("D2C mock server dependencies are not installed");
28420
28586
  }
28421
28587
  if (options.installDependencies !== false) await install(projectDir, options.signal);
@@ -28458,7 +28624,7 @@ ${managed.output().slice(-4e3)}`);
28458
28624
  // src/d2c/product.ts
28459
28625
  import { createHash as createHash24, randomUUID as randomUUID27 } from "crypto";
28460
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";
28461
- import { join as join38 } from "path";
28627
+ import { join as join39 } from "path";
28462
28628
  import { z as z44 } from "zod";
28463
28629
 
28464
28630
  // src/e2e/prd-governance.ts
@@ -28613,13 +28779,13 @@ var PlanSchema2 = z44.object({
28613
28779
  }).strict();
28614
28780
  function d2cProductDirectory(workspace, task) {
28615
28781
  if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
28616
- return join38(taskDir(workspace, task), "product");
28782
+ return join39(taskDir(workspace, task), "product");
28617
28783
  }
28618
28784
  function d2cProductPrototypeDirectory(workspace, task) {
28619
- return join38(d2cProductDirectory(workspace, task), "prototype");
28785
+ return join39(d2cProductDirectory(workspace, task), "prototype");
28620
28786
  }
28621
28787
  function planPath(workspace, task) {
28622
- return join38(taskDir(workspace, task), "product-plan.json");
28788
+ return join39(taskDir(workspace, task), "product-plan.json");
28623
28789
  }
28624
28790
  function resolveD2cProductTechnology(requirement, fallbackFramework = "vue") {
28625
28791
  const frontendCandidates = [
@@ -28802,16 +28968,16 @@ async function readD2cProductPlan(workspace, task) {
28802
28968
  }
28803
28969
  async function discoverD2cProductArtifacts(workspace, plan) {
28804
28970
  const product = d2cProductDirectory(workspace, plan.task);
28805
- const prdUpdatedAt = await fileTimestamp(join38(product, "prd.md"));
28806
- const prdHash = await fileHash(join38(product, "prd.md"));
28807
- const prototypeUpdatedAt = await fileTimestamp(join38(product, "prototype", "index.html"));
28808
- const prototypeHash = await fileHash(join38(product, "prototype", "index.html"));
28809
- 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");
28810
28976
  let manifestUpdatedAt = await fileTimestamp(expectedManifest);
28811
28977
  if (manifestUpdatedAt === void 0) {
28812
- const misplacedManifest = join38(product, "interaction-manifest.json");
28978
+ const misplacedManifest = join39(product, "interaction-manifest.json");
28813
28979
  if (await fileTimestamp(misplacedManifest) !== void 0) {
28814
- await mkdir25(join38(product, "prototype"), { recursive: true });
28980
+ await mkdir25(join39(product, "prototype"), { recursive: true });
28815
28981
  await copyFile2(misplacedManifest, expectedManifest);
28816
28982
  manifestUpdatedAt = await fileTimestamp(expectedManifest);
28817
28983
  }
@@ -28844,8 +29010,8 @@ async function readD2cProductPlanView(workspace, task) {
28844
29010
  if (stored === void 0) return void 0;
28845
29011
  const plan = await discoverD2cProductArtifacts(workspace, stored);
28846
29012
  if (plan.revision !== stored.revision) await writeD2cProductPlan(workspace, plan);
28847
- const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(join38(taskDir(workspace, task), plan.prd.path), "utf8");
28848
- 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");
28849
29015
  const validationError = plan.phase === "design-generating" && await fileTimestamp(manifestPath) !== void 0 ? await validateAndNormalizePrototypeManifest(manifestPath) : void 0;
28850
29016
  return {
28851
29017
  plan,
@@ -29004,7 +29170,7 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
29004
29170
 
29005
29171
  // src/e2e/delivery-run.ts
29006
29172
  import { createHash as createHash26 } from "crypto";
29007
- import { join as join39 } from "path";
29173
+ import { join as join40 } from "path";
29008
29174
  import { z as z45 } from "zod";
29009
29175
  var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
29010
29176
  var ArtifactSchema = z45.object({
@@ -29121,7 +29287,7 @@ function completeDeliveryNode(run, node, outputs, now = /* @__PURE__ */ new Date
29121
29287
  return { ...run, nodes, updatedAt: timestamp2 };
29122
29288
  }
29123
29289
  function deliveryRunPath(workspace, task) {
29124
- return join39(taskDir(workspace, task), "delivery-run.json");
29290
+ return join40(taskDir(workspace, task), "delivery-run.json");
29125
29291
  }
29126
29292
  function decodeDeliveryRun(raw) {
29127
29293
  return DeliveryRunSchema.parse(JSON.parse(raw));
@@ -29164,7 +29330,7 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
29164
29330
  // src/skills/manager.ts
29165
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";
29166
29332
  import { homedir as homedir3 } from "os";
29167
- 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";
29168
29334
  import { stringify } from "yaml";
29169
29335
  var SkillManager = class {
29170
29336
  #workspace;
@@ -29187,7 +29353,7 @@ var SkillManager = class {
29187
29353
  async create(draft) {
29188
29354
  const value = parseDraft(draft);
29189
29355
  const projectRoot = this.#projectRoot();
29190
- const directory = join40(projectRoot, value.name);
29356
+ const directory = join41(projectRoot, value.name);
29191
29357
  await mkdir26(projectRoot, { recursive: true, mode: 448 });
29192
29358
  try {
29193
29359
  await mkdir26(directory, { mode: 448 });
@@ -29196,7 +29362,7 @@ var SkillManager = class {
29196
29362
  throw error;
29197
29363
  }
29198
29364
  try {
29199
- 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 });
29200
29366
  await this.#persistEnabled(value.name, true);
29201
29367
  return this.get(value.name);
29202
29368
  } catch (error) {
@@ -29214,7 +29380,7 @@ var SkillManager = class {
29214
29380
  throw error;
29215
29381
  });
29216
29382
  if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`Project skill path is not a real directory: ${validName}`);
29217
- const skillFile = join40(directory, "SKILL.md");
29383
+ const skillFile = join41(directory, "SKILL.md");
29218
29384
  const fileInfo = await lstat13(skillFile);
29219
29385
  if (!fileInfo.isFile() || fileInfo.isSymbolicLink()) throw new Error(`Project skill file is not a real file: ${validName}`);
29220
29386
  await writeFile16(skillFile, renderSkill(value), { encoding: "utf8", mode: 384 });
@@ -29246,12 +29412,12 @@ var SkillManager = class {
29246
29412
  async #registry() {
29247
29413
  const loaded = await loadConfig({ cwd: this.#workspace, home: this.#home });
29248
29414
  const [globalPluginRoots, projectPluginRoots] = await Promise.all([
29249
- discoverPluginSkillRoots(join40(this.#home, ".flavor-code", "plugins")),
29250
- discoverPluginSkillRoots(join40(this.#workspace, ".flavor", "plugins"))
29415
+ discoverPluginSkillRoots(join41(this.#home, ".flavor-code", "plugins")),
29416
+ discoverPluginSkillRoots(join41(this.#workspace, ".flavor", "plugins"))
29251
29417
  ]);
29252
29418
  return {
29253
29419
  registry: new SkillRegistry({
29254
- globalRoots: [join40(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
29420
+ globalRoots: [join41(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
29255
29421
  projectRoots: [this.#projectRoot(), ...projectPluginRoots]
29256
29422
  }),
29257
29423
  disabled: new Set(loaded.config.skills.disabled)
@@ -29267,10 +29433,10 @@ var SkillManager = class {
29267
29433
  };
29268
29434
  }
29269
29435
  #projectRoot() {
29270
- return join40(this.#workspace, ".flavor", "skills");
29436
+ return join41(this.#workspace, ".flavor", "skills");
29271
29437
  }
29272
29438
  #projectSkillDirectory(name) {
29273
- return join40(this.#projectRoot(), name);
29439
+ return join41(this.#projectRoot(), name);
29274
29440
  }
29275
29441
  };
29276
29442
  function parseName(value) {
@@ -29311,9 +29477,9 @@ async function discoverPluginSkillRoots(directory) {
29311
29477
  const roots = [];
29312
29478
  for (const entry2 of entries) {
29313
29479
  if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue;
29314
- const pluginRoot = await realpath13(join40(directory, entry2.name));
29480
+ const pluginRoot = await realpath13(join41(directory, entry2.name));
29315
29481
  try {
29316
- 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")));
29317
29483
  for (const contribution of manifest.contributes.skillRoots) {
29318
29484
  const candidate = resolve34(pluginRoot, contribution.path);
29319
29485
  const info = await lstat13(candidate);
@@ -29388,11 +29554,11 @@ async function createProjectMemoryManager(options) {
29388
29554
  }
29389
29555
 
29390
29556
  // src/mcp/config-manager.ts
29391
- import { join as join41 } from "path";
29557
+ import { join as join42 } from "path";
29392
29558
  var ProjectMcpConfigManager = class {
29393
29559
  path;
29394
29560
  constructor(workspace) {
29395
- this.path = join41(workspace, ".flavor", "flavor.json");
29561
+ this.path = join42(workspace, ".flavor", "flavor.json");
29396
29562
  }
29397
29563
  async list() {
29398
29564
  const document = await readRecoverableFile(this.path, (raw) => parseDocument2(this.path, raw));
@@ -29489,7 +29655,7 @@ function isRecord7(value) {
29489
29655
  }
29490
29656
 
29491
29657
  // src/desktop/model-config.ts
29492
- import { join as join42 } from "path";
29658
+ import { join as join43 } from "path";
29493
29659
  var DEFAULT_DESKTOP_MODELS = [
29494
29660
  {
29495
29661
  id: "anthropic:deepseek-v4-pro",
@@ -29510,7 +29676,7 @@ var DEFAULT_DESKTOP_MODELS = [
29510
29676
  ];
29511
29677
  async function loadDesktopModels(workspace, home) {
29512
29678
  try {
29513
- const tokens2 = await createFileTokenStore(join42(home, ".flavor-code", "auth.json")).load();
29679
+ const tokens2 = await createFileTokenStore(join43(home, ".flavor-code", "auth.json")).load();
29514
29680
  const pkceModels = [];
29515
29681
  const knownPkce = /* @__PURE__ */ new Set();
29516
29682
  for (const token of Object.values(tokens2)) {
@@ -29655,33 +29821,33 @@ function pngDataUrl(png) {
29655
29821
  }
29656
29822
  async function detectD2cFramework(workspace, task) {
29657
29823
  try {
29658
- 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"));
29659
29825
  return pkg.dependencies?.react === void 0 ? "vue" : "react";
29660
29826
  } catch {
29661
29827
  return "vue";
29662
29828
  }
29663
29829
  }
29664
29830
  function integrationDirectory(workspace, task) {
29665
- return join43(workspace, ".flavor", "d2c", task, "integration");
29831
+ return join44(workspace, ".flavor", "d2c", task, "integration");
29666
29832
  }
29667
29833
  var D2C_BACKEND_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".cfg", ".env", ".ini", ".js", ".json", ".lock", ".mjs", ".py", ".toml", ".txt", ".yaml", ".yml"]);
29668
29834
  var D2C_BACKEND_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".venv", "__pycache__", "data", "node_modules", "venv"]);
29669
29835
  async function d2cBackendSourceFingerprint(project) {
29670
- const runtimeManifest = join43(project, "server", "flavor-runtime.json");
29836
+ const runtimeManifest = join44(project, "server", "flavor-runtime.json");
29671
29837
  const realBackend = (await stat15(runtimeManifest).catch(() => void 0))?.isFile() === true;
29672
- const root = realBackend ? join43(project, "server") : join43(project, "mock");
29838
+ const root = realBackend ? join44(project, "server") : join44(project, "mock");
29673
29839
  if ((await stat15(root).catch(() => void 0))?.isDirectory() !== true) return void 0;
29674
29840
  const files = [];
29675
29841
  const visit = async (directory) => {
29676
29842
  const entries = await readdir12(directory, { withFileTypes: true });
29677
29843
  for (const entry2 of entries) {
29678
29844
  if (entry2.isDirectory()) {
29679
- 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));
29680
29846
  continue;
29681
29847
  }
29682
29848
  if (!entry2.isFile()) continue;
29683
29849
  const extension2 = entry2.name.includes(".") ? `.${entry2.name.split(".").pop().toLowerCase()}` : "";
29684
- 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));
29685
29851
  }
29686
29852
  };
29687
29853
  await visit(root);
@@ -30235,7 +30401,7 @@ var DesktopRuntimeController = class {
30235
30401
  if (workflow === void 0 || !reviewProgress(workflow).complete || workflow.stage === "visual-review") {
30236
30402
  throw new Error("Complete visual review before importing OpenAPI");
30237
30403
  }
30238
- const moduleManifest = join43(d2cOutputDirectory(workspace, task), "d2c.modules.json");
30404
+ const moduleManifest = join44(d2cOutputDirectory(workspace, task), "d2c.modules.json");
30239
30405
  const moduleManifestInfo = await stat15(moduleManifest).catch(() => void 0);
30240
30406
  if (moduleManifestInfo === void 0 || !moduleManifestInfo.isFile()) {
30241
30407
  throw new Error("D2C implementation must write d2c.modules.json before API mapping; ask the agent to split the project into modules first");
@@ -30248,8 +30414,8 @@ var DesktopRuntimeController = class {
30248
30414
  await mkdir27(dir, { recursive: true });
30249
30415
  const hash = createHash27("sha256").update(raw).digest("hex");
30250
30416
  await Promise.all([
30251
- writeFile17(join43(dir, "swagger.json"), raw),
30252
- 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)}
30253
30419
  `)
30254
30420
  ]);
30255
30421
  const next = {
@@ -30284,7 +30450,7 @@ var DesktopRuntimeController = class {
30284
30450
  let workflow = await readWorkflow(workspace, task);
30285
30451
  if (workflow?.openapi === void 0 && workflow !== void 0 && reviewProgress(workflow).complete && await readD2cProductPlanView(workspace, task) !== void 0) {
30286
30452
  const modules = await readD2cModules(workspace, task);
30287
- const productOpenApiPath = join43(taskDir(workspace, task), "product", "openapi.json");
30453
+ const productOpenApiPath = join44(taskDir(workspace, task), "product", "openapi.json");
30288
30454
  const authored = await readFile35(productOpenApiPath, "utf8").catch(() => void 0);
30289
30455
  const generated = authored === void 0;
30290
30456
  const prepared = await this.#storeD2cOpenApi(
@@ -30297,7 +30463,7 @@ var DesktopRuntimeController = class {
30297
30463
  return prepared;
30298
30464
  }
30299
30465
  if (workflow?.openapi === void 0 || workflow.mappings === void 0) return void 0;
30300
- 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"));
30301
30467
  return { workflow, document, mappings: workflow.mappings };
30302
30468
  }
30303
30469
  async confirmD2cMapping(task, moduleId, operationKey) {
@@ -30364,7 +30530,7 @@ var DesktopRuntimeController = class {
30364
30530
  ].join("\n");
30365
30531
  const delivery = await readDeliveryRun(workspace, task);
30366
30532
  if (delivery?.nodes.api.status === "running") {
30367
- 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)))));
30368
30534
  const completed = await updateDeliveryRun(
30369
30535
  workspace,
30370
30536
  task,
@@ -30391,7 +30557,7 @@ var DesktopRuntimeController = class {
30391
30557
  try {
30392
30558
  const fingerprint2 = await d2cBackendSourceFingerprint(project);
30393
30559
  if (fingerprint2 !== void 0) this.#d2cMockSourceFingerprints.set(task, fingerprint2);
30394
- const environmentPath = join43(project, ".env.local");
30560
+ const environmentPath = join44(project, ".env.local");
30395
30561
  const existingEnvironment = await readFile35(environmentPath, "utf8").catch(() => "");
30396
30562
  const environment = `${existingEnvironment.split(/\r?\n/).filter((line) => !/^\s*VITE_API_BASE_URL\s*=/.test(line)).join("\n").trim()}
30397
30563
  VITE_API_BASE_URL=${running.url}
@@ -30472,7 +30638,7 @@ VITE_API_BASE_URL=${running.url}
30472
30638
  const readyPreview = this.#d2cPreviews.get(task);
30473
30639
  const readyMock = this.#d2cMocks.get(task);
30474
30640
  if (readyPreview === void 0 || readyMock === void 0) throw new Error("D2C preview or backend service is unavailable after synchronization");
30475
- const manifestPath = join43(taskDir(workspace, task), "design", "interaction-manifest.json");
30641
+ const manifestPath = join44(taskDir(workspace, task), "design", "interaction-manifest.json");
30476
30642
  const manifest = parseInteractionManifest(await readFile35(manifestPath, "utf8"));
30477
30643
  const baseline = await this.#verifyAcceptanceBaseline(workspace, task);
30478
30644
  const coverage = baseline === void 0 ? void 0 : verifyRequirementCoverage(baseline.approvedPrd.criteria, manifest);
@@ -30484,7 +30650,7 @@ VITE_API_BASE_URL=${running.url}
30484
30650
  await mkdir27(planDir, { recursive: true });
30485
30651
  try {
30486
30652
  const observations = await this.#observeD2cPages(manifest, readyPreview.url);
30487
- 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);
30488
30654
  const apiContext = JSON.stringify({ openapi: workflow.openapi, mappings: workflow.mappings }, null, 2);
30489
30655
  const plan = await this.#d2cJudge.planInteractions({ task, seed: manifest, observations, ...prd === void 0 ? {} : { prd }, apiContext });
30490
30656
  executableManifest = mergeInteractionManifests(manifest, plan.manifest);
@@ -30495,9 +30661,9 @@ VITE_API_BASE_URL=${running.url}
30495
30661
  plannedScenarios: executableManifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
30496
30662
  pageAnalyses: plan.pageAnalyses
30497
30663
  };
30498
- 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)}
30499
30665
  `, { encoding: "utf8", mode: 384 });
30500
- await rm15(join43(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
30666
+ await rm15(join44(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
30501
30667
  } catch (cause) {
30502
30668
  const detail = message(cause);
30503
30669
  review = {
@@ -30505,8 +30671,8 @@ VITE_API_BASE_URL=${running.url}
30505
30671
  plannedScenarios: manifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
30506
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}`
30507
30673
  };
30508
- await rm15(join43(planDir, "autonomous-interaction-plan.json"), { force: true });
30509
- 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({
30510
30676
  schema: 1,
30511
30677
  stage: "planning",
30512
30678
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -30520,13 +30686,13 @@ VITE_API_BASE_URL=${running.url}
30520
30686
  let result = { ...rawResult, evidenceMode: review.mode };
30521
30687
  if (coverage !== void 0) {
30522
30688
  const evidence = createAcceptanceEvidence(coverage, result);
30523
- 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)}
30524
30690
  `, { encoding: "utf8", mode: 384 });
30525
30691
  if (!evidence.passed) result = { ...result, passed: false, failures: Math.max(1, result.failures) };
30526
30692
  }
30527
30693
  const dir = integrationDirectory(workspace, task);
30528
30694
  await mkdir27(dir, { recursive: true });
30529
- 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)}
30530
30696
  `);
30531
30697
  const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyInteractionRun(current, result));
30532
30698
  return { workflow: next, result, review };
@@ -30539,7 +30705,7 @@ VITE_API_BASE_URL=${running.url}
30539
30705
  if (mock === void 0 || !mock.exited() || result.failures === 0) return result;
30540
30706
  const output = mock.output().trim();
30541
30707
  try {
30542
- const dir = join43(d2cOutputDirectory(workspace, task), "mock-server.log");
30708
+ const dir = join44(d2cOutputDirectory(workspace, task), "mock-server.log");
30543
30709
  await writeFile17(dir, `${output}
30544
30710
  `);
30545
30711
  } catch {
@@ -30634,7 +30800,7 @@ VITE_API_BASE_URL=${running.url}
30634
30800
  if (current === void 0) throw new Error("D2C product plan is unavailable");
30635
30801
  if (current.plan.approvedPrd !== void 0) throw new Error("The approved PRD is immutable and cannot be edited");
30636
30802
  if (current.plan.phase !== "prd-review") throw new Error("PRD sections can only be edited during PRD review");
30637
- const path = join43(taskDir(workspace, task), "product", "prd.md");
30803
+ const path = join44(taskDir(workspace, task), "product", "prd.md");
30638
30804
  const updated = await updatePrdSectionFile(path, { sectionId, body, expectedHash });
30639
30805
  const now = (/* @__PURE__ */ new Date()).toISOString();
30640
30806
  await writeD2cProductPlan(workspace, {
@@ -30654,15 +30820,15 @@ VITE_API_BASE_URL=${running.url}
30654
30820
  async #assertProductPrdLocked(workspace, task) {
30655
30821
  const current = await readD2cProductPlanView(workspace, task);
30656
30822
  if (current?.plan.approvedPrd === void 0) return;
30657
- 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);
30658
30824
  }
30659
30825
  async #acceptancePaths(workspace, task) {
30660
- const product = join43(taskDir(workspace, task), "product");
30661
- const openapi = join43(product, "openapi.json");
30826
+ const product = join44(taskDir(workspace, task), "product");
30827
+ const openapi = join44(product, "openapi.json");
30662
30828
  return {
30663
- prd: join43(product, "prd.md"),
30664
- prototype: join43(product, "prototype", "index.html"),
30665
- 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"),
30666
30832
  ...await stat15(openapi).catch(() => void 0) === void 0 ? {} : { openapi }
30667
30833
  };
30668
30834
  }
@@ -30670,17 +30836,17 @@ VITE_API_BASE_URL=${running.url}
30670
30836
  const product = await readD2cProductPlanView(workspace, task);
30671
30837
  if (product?.plan.approvedPrd === void 0) return void 0;
30672
30838
  await this.#assertProductPrdLocked(workspace, task);
30673
- 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"));
30674
30840
  await verifyAcceptanceBaseline(await this.#acceptancePaths(workspace, task), baseline);
30675
- const design = join43(taskDir(workspace, task), "design");
30841
+ const design = join44(taskDir(workspace, task), "design");
30676
30842
  await Promise.all([
30677
- verifyAcceptanceArtifact(join43(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
30678
- 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")
30679
30845
  ]);
30680
30846
  const workflow = await readWorkflow(workspace, task);
30681
30847
  if (workflow?.openapi !== void 0) {
30682
30848
  await verifyAcceptanceArtifact(
30683
- join43(taskDir(workspace, task), "integration", "swagger.json"),
30849
+ join44(taskDir(workspace, task), "integration", "swagger.json"),
30684
30850
  { hash: workflow.openapi.hash },
30685
30851
  "runtime OpenAPI contract"
30686
30852
  );
@@ -30701,14 +30867,14 @@ VITE_API_BASE_URL=${running.url}
30701
30867
  throw new Error("D2C PRD is empty and cannot be accepted");
30702
30868
  }
30703
30869
  if (stage === "design" && accepted) {
30704
- const manifest = join43(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
30870
+ const manifest = join44(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
30705
30871
  const normalizedManifest = parseInteractionManifest(await readFile35(manifest, "utf8"));
30706
30872
  if (current.plan.approvedPrd === void 0) throw new Error("Approve the PRD before confirming the design");
30707
30873
  verifyRequirementCoverage(current.plan.approvedPrd.criteria, normalizedManifest);
30708
30874
  await writeFile17(manifest, `${JSON.stringify(normalizedManifest, null, 2)}
30709
30875
  `, { encoding: "utf8", mode: 384 });
30710
30876
  const baseline = await captureAcceptanceBaseline(await this.#acceptancePaths(workspace, task), current.plan.approvedPrd);
30711
- 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)}
30712
30878
  `, { encoding: "utf8", mode: 384 });
30713
30879
  }
30714
30880
  const plan = await writeD2cProductPlan(
@@ -30742,8 +30908,8 @@ VITE_API_BASE_URL=${running.url}
30742
30908
  const imported = await this.importD2cDesign(task, d2cProductPrototypeDirectory(workspace, task));
30743
30909
  const delivery = await readDeliveryRun(workspace, task);
30744
30910
  if (delivery !== void 0) {
30745
- const prototype = await readFile35(join43(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
30746
- 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");
30747
30913
  const completed = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "design", [
30748
30914
  artifactRef("product/prototype/index.html", prototype),
30749
30915
  artifactRef("product/prototype/interaction-manifest.json", interaction)
@@ -30827,10 +30993,10 @@ VITE_API_BASE_URL=${running.url}
30827
30993
  const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyQualityJudgment(current, judgment));
30828
30994
  const qualityContent = `${JSON.stringify(judgment, null, 2)}
30829
30995
  `;
30830
- 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 });
30831
30997
  const delivery = await readDeliveryRun(workspace, task);
30832
30998
  if (next.stage === "completed" && delivery?.nodes.acceptance.status === "running") {
30833
- 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");
30834
31000
  const accepted = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "acceptance", [
30835
31001
  artifactRef("integration/interaction-results.json", interactionContent),
30836
31002
  artifactRef("quality-judge.json", qualityContent)
@@ -31011,15 +31177,15 @@ function desktopWindowChrome(platform = process.platform) {
31011
31177
  }
31012
31178
 
31013
31179
  // src/utils/crash-guard.ts
31014
- import { mkdirSync as mkdirSync3, writeFileSync } from "fs";
31015
- import { join as join44 } from "path";
31180
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
31181
+ import { join as join45 } from "path";
31016
31182
  var installed = false;
31017
31183
  function installCrashGuard(options = {}) {
31018
31184
  if (installed) return;
31019
31185
  installed = true;
31020
31186
  const crashLogDir = () => {
31021
31187
  try {
31022
- return join44((options.workspace ?? process.cwd)(), ".flavor");
31188
+ return join45((options.workspace ?? process.cwd)(), ".flavor");
31023
31189
  } catch {
31024
31190
  return process.cwd();
31025
31191
  }
@@ -31027,9 +31193,9 @@ function installCrashGuard(options = {}) {
31027
31193
  const writeCrashLog = (kind, detail) => {
31028
31194
  try {
31029
31195
  const dir = crashLogDir();
31030
- mkdirSync3(dir, { recursive: true });
31031
- const path = join44(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
31032
- 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, [
31033
31199
  `kind: ${kind}`,
31034
31200
  `version: ${process.version}`,
31035
31201
  `platform: ${process.platform}`,
@@ -31221,7 +31387,7 @@ function safeGitTarget(value) {
31221
31387
  // src/desktop/worktree-manager.ts
31222
31388
  import { randomUUID as randomUUID28 } from "crypto";
31223
31389
  import { mkdir as mkdir28, readFile as readFile37, readdir as readdir13, rm as rm17, stat as stat16, writeFile as writeFile18 } from "fs/promises";
31224
- 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";
31225
31391
  var DesktopWorktreeManager = class {
31226
31392
  #repository;
31227
31393
  #storage;
@@ -31267,7 +31433,7 @@ var DesktopWorktreeManager = class {
31267
31433
  }
31268
31434
  const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
31269
31435
  try {
31270
- 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);
31271
31437
  if ((await stat16(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
31272
31438
  const status = await git2(stored.path, ["status", "--porcelain"]);
31273
31439
  const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
@@ -31331,7 +31497,7 @@ function validateStored(value, repository, storage) {
31331
31497
  // src/desktop/workbench-service.ts
31332
31498
  import { readFile as readFile38, readdir as readdir14, stat as stat17 } from "fs/promises";
31333
31499
  import { homedir as homedir5 } from "os";
31334
- import { join as join46, resolve as resolve39 } from "path";
31500
+ import { join as join47, resolve as resolve39 } from "path";
31335
31501
  var MAX_TEXT = 128e3;
31336
31502
  var MAX_ITEMS = 200;
31337
31503
  var DesktopWorkbenchService = class {
@@ -31364,11 +31530,11 @@ var DesktopWorkbenchService = class {
31364
31530
  return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
31365
31531
  }
31366
31532
  async #goals() {
31367
- const root = join46(this.#workspace, ".flavor", "goals");
31533
+ const root = join47(this.#workspace, ".flavor", "goals");
31368
31534
  const names = await readdir14(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
31369
31535
  const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
31370
31536
  try {
31371
- return GoalStateSchema.parse(JSON.parse(await boundedRead2(join46(root, name))));
31537
+ return GoalStateSchema.parse(JSON.parse(await boundedRead2(join47(root, name))));
31372
31538
  } catch {
31373
31539
  return void 0;
31374
31540
  }
@@ -31378,7 +31544,7 @@ var DesktopWorkbenchService = class {
31378
31544
  async #instructions() {
31379
31545
  const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
31380
31546
  const items = await Promise.all(names.map(async (name) => {
31381
- const path = join46(this.#workspace, name);
31547
+ const path = join47(this.#workspace, name);
31382
31548
  try {
31383
31549
  return { name, path, content: redactErrorText(await boundedRead2(path)) };
31384
31550
  } catch (error) {
@@ -31391,9 +31557,9 @@ var DesktopWorkbenchService = class {
31391
31557
  async #permissionFiles() {
31392
31558
  const candidates = [
31393
31559
  ...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve39(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
31394
- { tier: "user", path: join46(this.#home, ".flavor-code", "permissions.json"), content: "" },
31395
- { tier: "project", path: join46(this.#workspace, ".flavor", "permissions.json"), content: "" },
31396
- { 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: "" }
31397
31563
  ];
31398
31564
  const values = await Promise.all(candidates.map(async (item) => {
31399
31565
  try {
@@ -31408,7 +31574,7 @@ var DesktopWorkbenchService = class {
31408
31574
  async #audit() {
31409
31575
  let raw;
31410
31576
  try {
31411
- raw = await boundedRead2(join46(this.#workspace, ".flavor", "audit.jsonl"));
31577
+ raw = await boundedRead2(join47(this.#workspace, ".flavor", "audit.jsonl"));
31412
31578
  } catch (error) {
31413
31579
  if (error.code === "ENOENT") return [];
31414
31580
  throw error;
@@ -31423,7 +31589,7 @@ var DesktopWorkbenchService = class {
31423
31589
  }).reverse();
31424
31590
  }
31425
31591
  async #context(sessionId) {
31426
- const usage = await readJsonLines2(join46(this.#workspace, ".flavor", "usage.jsonl"));
31592
+ const usage = await readJsonLines2(join47(this.#workspace, ".flavor", "usage.jsonl"));
31427
31593
  if (sessionId === void 0) return { visibility: [], usage };
31428
31594
  try {
31429
31595
  const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
@@ -31463,14 +31629,14 @@ function redact2(value, key = "") {
31463
31629
 
31464
31630
  // src/desktop/main.ts
31465
31631
  installCrashGuard();
31466
- var moduleDirectory = dirname24(fileURLToPath4(import.meta.url));
31632
+ var moduleDirectory = dirname25(fileURLToPath4(import.meta.url));
31467
31633
  var developmentUrl = process.env.FLAVOR_DESKTOP_DEV_URL;
31468
31634
  var mainWindow;
31469
31635
  var appMenu;
31470
31636
  var quitting = false;
31471
31637
  function logStartup(step, detail) {
31472
31638
  try {
31473
- const logPath = join47(dirname24(process.execPath), "flavor-code-startup.log");
31639
+ const logPath = join48(dirname25(process.execPath), "flavor-code-startup.log");
31474
31640
  const ts = (/* @__PURE__ */ new Date()).toISOString();
31475
31641
  appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
31476
31642
  `);
@@ -31495,7 +31661,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
31495
31661
  };
31496
31662
  });
31497
31663
  var d2cJudgeClient = createD2cJudgeClient();
31498
- var judgeStore = () => createD2cJudgeConfigStore(join47(app.getPath("userData"), "d2c-judge.json"));
31664
+ var judgeStore = () => createD2cJudgeConfigStore(join48(app.getPath("userData"), "d2c-judge.json"));
31499
31665
  function judgeImage(png) {
31500
31666
  const image = nativeImage.createFromBuffer(png);
31501
31667
  const size = image.getSize();
@@ -31521,7 +31687,7 @@ function sessionMetaKey(workspace, sessionId) {
31521
31687
  }
31522
31688
  function worktreeManager(workspace) {
31523
31689
  const key = createHash28("sha256").update(resolve40(workspace)).digest("hex").slice(0, 20);
31524
- 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) });
31525
31691
  }
31526
31692
  function activeWorkingDirectory() {
31527
31693
  if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
@@ -31666,7 +31832,7 @@ function createDesktopController(workspace, owner2) {
31666
31832
  alias: `desktop-${basename11(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
31667
31833
  optional: true,
31668
31834
  startBroker: (address) => {
31669
- 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");
31670
31836
  return spawnPalBrokerProcess({
31671
31837
  address,
31672
31838
  executable: process.execPath,
@@ -31688,7 +31854,7 @@ function createDesktopController(workspace, owner2) {
31688
31854
  var detachedController = createDesktopController();
31689
31855
  var controller = detachedController;
31690
31856
  function statePath() {
31691
- return join47(app.getPath("userData"), "desktop-state.json");
31857
+ return join48(app.getPath("userData"), "desktop-state.json");
31692
31858
  }
31693
31859
  async function loadPersistedProjects() {
31694
31860
  try {
@@ -31861,7 +32027,7 @@ async function chooseAndOpenWorkspace() {
31861
32027
  var cachedAppIconDataUrl;
31862
32028
  function appIconDataUrl() {
31863
32029
  if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
31864
- const icon = nativeImage.createFromPath(join47(app.getAppPath(), "assets", "icon.png"));
32030
+ const icon = nativeImage.createFromPath(join48(app.getAppPath(), "assets", "icon.png"));
31865
32031
  if (icon.isEmpty()) return void 0;
31866
32032
  cachedAppIconDataUrl = icon.toDataURL();
31867
32033
  return cachedAppIconDataUrl;
@@ -32279,7 +32445,7 @@ function applicationMenu() {
32279
32445
  ]);
32280
32446
  }
32281
32447
  async function createWindow() {
32282
- const rendererPath = join47(app.getAppPath(), "dist", "desktop-renderer", "index.html");
32448
+ const rendererPath = join48(app.getAppPath(), "dist", "desktop-renderer", "index.html");
32283
32449
  mainWindow = new BrowserWindow2({
32284
32450
  title: "Flavor Code",
32285
32451
  width: 1280,
@@ -32290,14 +32456,14 @@ async function createWindow() {
32290
32456
  show: false,
32291
32457
  ...desktopWindowChrome(),
32292
32458
  webPreferences: {
32293
- preload: join47(moduleDirectory, "preload.cjs"),
32459
+ preload: join48(moduleDirectory, "preload.cjs"),
32294
32460
  contextIsolation: true,
32295
32461
  nodeIntegration: false,
32296
32462
  sandbox: false,
32297
32463
  webSecurity: true
32298
32464
  }
32299
32465
  });
32300
- const iconPath = join47(app.getAppPath(), "assets", "icon.png");
32466
+ const iconPath = join48(app.getAppPath(), "assets", "icon.png");
32301
32467
  const icon = nativeImage.createFromPath(iconPath);
32302
32468
  if (!icon.isEmpty()) {
32303
32469
  mainWindow.setIcon(icon);