flavor-code 1.3.15 → 1.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/agent/loop.d.ts +10 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/{app-DDYOKGCX.js → app-VPS5QIRK.js} +79 -7
- package/dist/{chunk-F2HMO5Q2.js → chunk-36UKDXPF.js} +9 -1
- package/dist/{chunk-WB4IJXFT.js → chunk-BKOOYSGH.js} +197 -35
- package/dist/chunk-CIVPKHF4.js +53 -0
- package/dist/{chunk-JFKRX2KG.js → chunk-HJOCNKPN.js} +1 -1
- package/dist/{chunk-TQXNF337.js → chunk-RV3PTIQE.js} +1 -1
- package/dist/{claude-ink-ORUA7GWG.js → claude-ink-AUZBJ3BE.js} +1 -1
- package/dist/cli-main.js +7 -6
- package/dist/cli.js +51 -22
- package/dist/context/manager.d.ts +7 -0
- package/dist/desktop/main.js +375 -195
- package/dist/desktop-renderer/assets/index-CxOMqdQf.js +154 -0
- package/dist/desktop-renderer/assets/{interactive-terminal-Bn8G5Pig.js → interactive-terminal-BK8GS4b3.js} +1 -1
- package/dist/desktop-renderer/index.html +1 -1
- package/dist/{load-LTTKVPJZ.js → load-DELQW42K.js} +1 -1
- package/dist/sdk/index.js +4 -3
- package/dist/session/tree.d.ts +3 -0
- package/dist/ui/transcript.d.ts +7 -0
- package/dist/utils/memory-restart.d.ts +34 -0
- package/package.json +4 -1
- package/dist/desktop-renderer/assets/index-BAHTmEUI.js +0 -154
package/dist/desktop/main.js
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
}
|
|
@@ -5506,7 +5519,7 @@ var ContextManager = class _ContextManager {
|
|
|
5506
5519
|
for (const key of DYNAMIC_SOURCE_ORDER) {
|
|
5507
5520
|
const value = next[key];
|
|
5508
5521
|
if (value === void 0 || value === this.#epoch.sources[key]) continue;
|
|
5509
|
-
updates.push(contextUpdateMessage(key, displaySourceValue(key, value)));
|
|
5522
|
+
updates.push(contextUpdateMessage(key, deltaSourceValue(this.#epoch.sources[key], displaySourceValue(key, value))));
|
|
5510
5523
|
this.#epoch.sources[key] = value;
|
|
5511
5524
|
}
|
|
5512
5525
|
for (const key of DYNAMIC_SOURCE_ORDER) {
|
|
@@ -5516,8 +5529,6 @@ var ContextManager = class _ContextManager {
|
|
|
5516
5529
|
}
|
|
5517
5530
|
if (updates.length === 0) return false;
|
|
5518
5531
|
this.#messages.push(...updates);
|
|
5519
|
-
this.#lastRecordedInputTokens = void 0;
|
|
5520
|
-
this.#estimatedTokensAtLastRecordedUsage = void 0;
|
|
5521
5532
|
return true;
|
|
5522
5533
|
}
|
|
5523
5534
|
snapshot() {
|
|
@@ -5533,7 +5544,7 @@ var ContextManager = class _ContextManager {
|
|
|
5533
5544
|
const legacySummary = snapshot4.summary?.role === "system" && snapshot4.summary.content.startsWith("Conversation summary\n") ? snapshot4.summary.content.slice("Conversation summary\n".length) : void 0;
|
|
5534
5545
|
this.#compact = snapshot4.compact === void 0 ? legacySummary === void 0 ? void 0 : { summary: legacySummary, compactedAt: (/* @__PURE__ */ new Date(0)).toISOString() } : { ...snapshot4.compact };
|
|
5535
5546
|
if (snapshot4.epoch !== void 0) this.#epoch = cloneEpoch(snapshot4.epoch);
|
|
5536
|
-
this.#visibilityLog = snapshot4.visibilityLog
|
|
5547
|
+
this.#visibilityLog = boundVisibilityLog(snapshot4.visibilityLog);
|
|
5537
5548
|
this.#activeTransientSystem.clear();
|
|
5538
5549
|
this.#messages = messages.map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
|
|
5539
5550
|
this.#lastRecordedInputTokens = void 0;
|
|
@@ -5577,6 +5588,7 @@ var ContextManager = class _ContextManager {
|
|
|
5577
5588
|
async prepareForModelCall(signal = new AbortController().signal) {
|
|
5578
5589
|
signal.throwIfAborted();
|
|
5579
5590
|
this.refreshContextSources();
|
|
5591
|
+
this.#dropStaleContextUpdates();
|
|
5580
5592
|
if (!this.needsCompaction()) return false;
|
|
5581
5593
|
const originalMessages = this.#messages;
|
|
5582
5594
|
const originalRecordedUsage = this.#lastRecordedInputTokens;
|
|
@@ -5589,8 +5601,6 @@ var ContextManager = class _ContextManager {
|
|
|
5589
5601
|
const microcompact = microcompactMessages(this.#messages, this.#compaction.microcompactKeepRecentToolResults);
|
|
5590
5602
|
if (microcompact.changed) {
|
|
5591
5603
|
this.#messages = microcompact.messages;
|
|
5592
|
-
this.#lastRecordedInputTokens = void 0;
|
|
5593
|
-
this.#estimatedTokensAtLastRecordedUsage = void 0;
|
|
5594
5604
|
}
|
|
5595
5605
|
if (!this.needsCompaction()) {
|
|
5596
5606
|
if (microcompact.changed) {
|
|
@@ -5676,6 +5686,26 @@ var ContextManager = class _ContextManager {
|
|
|
5676
5686
|
this.#reportCompactProgress(100);
|
|
5677
5687
|
return true;
|
|
5678
5688
|
}
|
|
5689
|
+
#dropStaleContextUpdates() {
|
|
5690
|
+
const latestBySource = /* @__PURE__ */ new Map();
|
|
5691
|
+
for (let index = this.#messages.length - 1; index >= 0; index -= 1) {
|
|
5692
|
+
const source = contextUpdateSource(this.#messages[index]);
|
|
5693
|
+
if (source === void 0 || latestBySource.has(source)) continue;
|
|
5694
|
+
latestBySource.set(source, index);
|
|
5695
|
+
}
|
|
5696
|
+
const next = [];
|
|
5697
|
+
let changed = false;
|
|
5698
|
+
this.#messages.forEach((message2, index) => {
|
|
5699
|
+
const source = contextUpdateSource(message2);
|
|
5700
|
+
if (source !== void 0 && latestBySource.get(source) !== index) {
|
|
5701
|
+
changed = true;
|
|
5702
|
+
return;
|
|
5703
|
+
}
|
|
5704
|
+
next.push(message2);
|
|
5705
|
+
});
|
|
5706
|
+
if (changed) this.#messages = next;
|
|
5707
|
+
return changed;
|
|
5708
|
+
}
|
|
5679
5709
|
#reportCompactProgress(percentage) {
|
|
5680
5710
|
const normalized = Math.max(0, Math.min(100, Math.floor(percentage / 10) * 10));
|
|
5681
5711
|
if (normalized === this.#lastCompactProgress) return;
|
|
@@ -5787,6 +5817,33 @@ function contextUpdateMessage(source, content) {
|
|
|
5787
5817
|
return { role: "system", content: `Context update [${source}]
|
|
5788
5818
|
${content}` };
|
|
5789
5819
|
}
|
|
5820
|
+
var CONTEXT_UPDATE_PREFIX = "Context update [";
|
|
5821
|
+
function contextUpdateSource(message2) {
|
|
5822
|
+
if (message2.role !== "system" || typeof message2.content !== "string") return void 0;
|
|
5823
|
+
if (!message2.content.startsWith(CONTEXT_UPDATE_PREFIX)) return void 0;
|
|
5824
|
+
const end = message2.content.indexOf("]\n", CONTEXT_UPDATE_PREFIX.length);
|
|
5825
|
+
if (end < 0) return void 0;
|
|
5826
|
+
return message2.content.slice(CONTEXT_UPDATE_PREFIX.length, end);
|
|
5827
|
+
}
|
|
5828
|
+
function deltaSourceValue(previous, display) {
|
|
5829
|
+
if (previous === void 0 || !display.startsWith(previous)) return display;
|
|
5830
|
+
return display.slice(previous.length).replace(/^\s+/, "") || display;
|
|
5831
|
+
}
|
|
5832
|
+
function limitVisibilityContent(content) {
|
|
5833
|
+
const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
|
|
5834
|
+
if (content.length <= limit) return content;
|
|
5835
|
+
return `${content.slice(0, limit)}
|
|
5836
|
+
...[truncated; original length: ${content.length} characters]...`;
|
|
5837
|
+
}
|
|
5838
|
+
function boundVisibilityLog(records) {
|
|
5839
|
+
if (records === void 0) return [];
|
|
5840
|
+
const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
|
|
5841
|
+
const bounded2 = records.map((item) => ({
|
|
5842
|
+
...item,
|
|
5843
|
+
...item.content.length <= limit ? {} : { content: limitVisibilityContent(item.content) }
|
|
5844
|
+
}));
|
|
5845
|
+
return bounded2.length > ContextManager.VISIBILITY_LOG_MAX_ENTRIES ? bounded2.slice(-ContextManager.VISIBILITY_LOG_MAX_ENTRIES) : bounded2;
|
|
5846
|
+
}
|
|
5790
5847
|
function cloneEpoch(epoch) {
|
|
5791
5848
|
return {
|
|
5792
5849
|
...epoch,
|
|
@@ -6081,6 +6138,9 @@ function reportProgress(callback, percentage) {
|
|
|
6081
6138
|
}
|
|
6082
6139
|
}
|
|
6083
6140
|
|
|
6141
|
+
// src/agent/loop.ts
|
|
6142
|
+
import { getHeapStatistics } from "v8";
|
|
6143
|
+
|
|
6084
6144
|
// src/models/structured.ts
|
|
6085
6145
|
import { z as z12 } from "zod";
|
|
6086
6146
|
var DEFAULT_RETRY_DELAYS = [1e3, 2e3, 4e3];
|
|
@@ -6498,6 +6558,18 @@ function envMaxIterations() {
|
|
|
6498
6558
|
if (!Number.isInteger(parsed) || parsed <= 0) return void 0;
|
|
6499
6559
|
return parsed;
|
|
6500
6560
|
}
|
|
6561
|
+
var MEMORY_PRESSURE_HEAP_RATIO = 0.8;
|
|
6562
|
+
function memoryPressureError(heapUsed, heapLimit) {
|
|
6563
|
+
if (heapLimit <= 0 || heapUsed < heapLimit * MEMORY_PRESSURE_HEAP_RATIO) return void 0;
|
|
6564
|
+
const mb = (bytes) => Math.round(bytes / 1048576);
|
|
6565
|
+
return {
|
|
6566
|
+
code: "memory_pressure",
|
|
6567
|
+
message: `Heap usage reached ${Math.round(heapUsed / heapLimit * 100)}% of the V8 limit (${mb(heapUsed)}MB of ${mb(heapLimit)}MB); stopping the current turn to avoid a crash. The saved session will reopen after restart.`
|
|
6568
|
+
};
|
|
6569
|
+
}
|
|
6570
|
+
function currentMemoryPressure() {
|
|
6571
|
+
return memoryPressureError(process.memoryUsage().heapUsed, getHeapStatistics().heap_size_limit);
|
|
6572
|
+
}
|
|
6501
6573
|
var AgentLoop = class {
|
|
6502
6574
|
#options;
|
|
6503
6575
|
#iterationLimitMode = "standard";
|
|
@@ -6568,6 +6640,11 @@ var AgentLoop = class {
|
|
|
6568
6640
|
yield { type: "error", error: { code: "cancelled", message: abortMessage(request.signal) } };
|
|
6569
6641
|
return;
|
|
6570
6642
|
}
|
|
6643
|
+
const pressure = currentMemoryPressure();
|
|
6644
|
+
if (pressure !== void 0) {
|
|
6645
|
+
yield { type: "error", error: pressure };
|
|
6646
|
+
return;
|
|
6647
|
+
}
|
|
6571
6648
|
const softLimit = Math.floor(maxIterations * this.#options.softLimitFactor);
|
|
6572
6649
|
if (!warned && iteration >= softLimit) {
|
|
6573
6650
|
warned = true;
|
|
@@ -6607,6 +6684,11 @@ var AgentLoop = class {
|
|
|
6607
6684
|
yield { type: "error", error: normalizeProviderError(error) };
|
|
6608
6685
|
return;
|
|
6609
6686
|
}
|
|
6687
|
+
const preparedPressure = currentMemoryPressure();
|
|
6688
|
+
if (preparedPressure !== void 0) {
|
|
6689
|
+
yield { type: "error", error: preparedPressure };
|
|
6690
|
+
return;
|
|
6691
|
+
}
|
|
6610
6692
|
let assistantText = "";
|
|
6611
6693
|
const collectedToolCalls = [];
|
|
6612
6694
|
const collectedThinking = [];
|
|
@@ -6630,6 +6712,11 @@ var AgentLoop = class {
|
|
|
6630
6712
|
tools: [...this.#options.tools],
|
|
6631
6713
|
...request.signal === void 0 ? {} : { signal: request.signal }
|
|
6632
6714
|
};
|
|
6715
|
+
const requestPressure = currentMemoryPressure();
|
|
6716
|
+
if (requestPressure !== void 0) {
|
|
6717
|
+
yield { type: "error", error: requestPressure };
|
|
6718
|
+
return;
|
|
6719
|
+
}
|
|
6633
6720
|
let before;
|
|
6634
6721
|
try {
|
|
6635
6722
|
before = await this.#options.hooks.emit({
|
|
@@ -12927,7 +13014,8 @@ var BoundedOutput = class {
|
|
|
12927
13014
|
#headLimit;
|
|
12928
13015
|
#tailLimit;
|
|
12929
13016
|
#head = Buffer.alloc(0);
|
|
12930
|
-
#
|
|
13017
|
+
#tailChunks = [];
|
|
13018
|
+
#tailBytes = 0;
|
|
12931
13019
|
#complete = Buffer.alloc(0);
|
|
12932
13020
|
#bytes = 0;
|
|
12933
13021
|
constructor(limit) {
|
|
@@ -12947,15 +13035,24 @@ var BoundedOutput = class {
|
|
|
12947
13035
|
const count = Math.min(this.#headLimit - this.#head.length, chunk.length);
|
|
12948
13036
|
this.#head = Buffer.concat([this.#head, chunk.subarray(0, count)]);
|
|
12949
13037
|
}
|
|
12950
|
-
if (this.#tailLimit > 0)
|
|
13038
|
+
if (this.#tailLimit > 0) {
|
|
13039
|
+
this.#tailChunks.push(chunk);
|
|
13040
|
+
this.#tailBytes += chunk.length;
|
|
13041
|
+
if (this.#tailBytes >= this.#tailLimit * 2) {
|
|
13042
|
+
const merged = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
13043
|
+
this.#tailChunks = [merged];
|
|
13044
|
+
this.#tailBytes = merged.length;
|
|
13045
|
+
}
|
|
13046
|
+
}
|
|
12951
13047
|
}
|
|
12952
13048
|
metadata() {
|
|
12953
13049
|
return { truncated: this.truncated, originalBytes: this.#bytes, limitBytes: this.#limit };
|
|
12954
13050
|
}
|
|
12955
13051
|
text() {
|
|
12956
13052
|
if (!this.truncated) return decodeOutput(this.#complete);
|
|
13053
|
+
const tail = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
12957
13054
|
const encoding = classifyUtf8(this.#complete) === "invalid" && process.platform === "win32" ? "gb18030" : "utf-8";
|
|
12958
|
-
return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(
|
|
13055
|
+
return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(tail, encoding)}`;
|
|
12959
13056
|
}
|
|
12960
13057
|
};
|
|
12961
13058
|
var AdaptiveOutputDecoder = class {
|
|
@@ -15406,6 +15503,8 @@ function appendThinkingText(previous, delta) {
|
|
|
15406
15503
|
}
|
|
15407
15504
|
|
|
15408
15505
|
// src/ui/transcript.ts
|
|
15506
|
+
var MAX_TRANSCRIPT_TURNS = 200;
|
|
15507
|
+
var MAX_TRANSCRIPT_CHARS = 2e6;
|
|
15409
15508
|
function createTranscriptState() {
|
|
15410
15509
|
return { completed: [], nextId: 1 };
|
|
15411
15510
|
}
|
|
@@ -15702,7 +15801,7 @@ function hydrateHistory(messages, compact) {
|
|
|
15702
15801
|
}
|
|
15703
15802
|
}
|
|
15704
15803
|
if (turn !== void 0) completed.push(turn);
|
|
15705
|
-
return { completed, nextId: completed.length + 1 };
|
|
15804
|
+
return { completed: boundedCompleted(completed), nextId: completed.length + 1 };
|
|
15706
15805
|
}
|
|
15707
15806
|
function transcriptFromLegacyConversation(messages, compact) {
|
|
15708
15807
|
return hydrateHistory(messages, compact);
|
|
@@ -15796,10 +15895,10 @@ function finishActive(state) {
|
|
|
15796
15895
|
if (state.active === void 0) return state;
|
|
15797
15896
|
const active = withoutModelActivity(state.active);
|
|
15798
15897
|
return {
|
|
15799
|
-
completed: [...state.completed, {
|
|
15898
|
+
completed: boundedCompleted([...state.completed, {
|
|
15800
15899
|
...active,
|
|
15801
15900
|
completedAt: active.completedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
15802
|
-
}],
|
|
15901
|
+
}]),
|
|
15803
15902
|
nextId: state.nextId,
|
|
15804
15903
|
...state.taskSnapshot === void 0 ? {} : { taskSnapshot: state.taskSnapshot }
|
|
15805
15904
|
};
|
|
@@ -15828,12 +15927,26 @@ function withoutModelActivity(turn, id) {
|
|
|
15828
15927
|
function restoreTranscriptState(state) {
|
|
15829
15928
|
const completed = state.completed.map(cloneTurn);
|
|
15830
15929
|
if (state.active !== void 0) completed.push(normalizeInterruptedTurn(cloneTurn(state.active)));
|
|
15930
|
+
const nextId = Math.max(state.nextId, ...completed.map((turn) => turn.id + 1), 1);
|
|
15831
15931
|
return {
|
|
15832
|
-
completed,
|
|
15833
|
-
nextId
|
|
15932
|
+
completed: boundedCompleted(completed),
|
|
15933
|
+
nextId,
|
|
15834
15934
|
...state.taskSnapshot === void 0 ? {} : { taskSnapshot: state.taskSnapshot }
|
|
15835
15935
|
};
|
|
15836
15936
|
}
|
|
15937
|
+
function boundedCompleted(input) {
|
|
15938
|
+
const recent = input.slice(-MAX_TRANSCRIPT_TURNS);
|
|
15939
|
+
let chars = 0;
|
|
15940
|
+
let start = recent.length;
|
|
15941
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
15942
|
+
const turn = recent[index];
|
|
15943
|
+
const size = JSON.stringify(turn).length;
|
|
15944
|
+
if (start < recent.length && chars + size > MAX_TRANSCRIPT_CHARS) break;
|
|
15945
|
+
chars += size;
|
|
15946
|
+
start = index;
|
|
15947
|
+
}
|
|
15948
|
+
return recent.slice(start);
|
|
15949
|
+
}
|
|
15837
15950
|
function compactionTurn(compact) {
|
|
15838
15951
|
const text = `Original execution steps before ${compact.compactedAt} are unavailable.`;
|
|
15839
15952
|
return {
|
|
@@ -16657,6 +16770,8 @@ function isCode9(error, code) {
|
|
|
16657
16770
|
}
|
|
16658
16771
|
|
|
16659
16772
|
// src/session/tree.ts
|
|
16773
|
+
var MAX_SESSION_HISTORY_NODES = 100;
|
|
16774
|
+
var MAX_SESSION_HISTORY_CONTEXT_CHARS = 2e6;
|
|
16660
16775
|
var SessionHistory = class _SessionHistory {
|
|
16661
16776
|
#path;
|
|
16662
16777
|
#checkpoints;
|
|
@@ -16668,8 +16783,8 @@ var SessionHistory = class _SessionHistory {
|
|
|
16668
16783
|
this.#path = join25(resolve21(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
16669
16784
|
this.#checkpoints = new WorkspaceCheckpointStore({ workspace: options.workspace });
|
|
16670
16785
|
this.#restoreContext = options.restoreContext;
|
|
16671
|
-
this.#nodes = document.nodes;
|
|
16672
|
-
this.#leafId = document.leafId;
|
|
16786
|
+
this.#nodes = boundedNodes(document.nodes);
|
|
16787
|
+
this.#leafId = document.leafId !== null && this.#nodes.some((node) => node.id === document.leafId) ? document.leafId : this.#nodes.at(-1)?.id ?? null;
|
|
16673
16788
|
}
|
|
16674
16789
|
static async open(options) {
|
|
16675
16790
|
const path = join25(resolve21(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
@@ -16681,7 +16796,9 @@ var SessionHistory = class _SessionHistory {
|
|
|
16681
16796
|
} catch (error) {
|
|
16682
16797
|
if (!isCode10(error, "ENOENT")) throw error;
|
|
16683
16798
|
}
|
|
16684
|
-
|
|
16799
|
+
const history = new _SessionHistory(options, document);
|
|
16800
|
+
if (history.#nodes.length !== document.nodes.length) await history.#persist();
|
|
16801
|
+
return history;
|
|
16685
16802
|
}
|
|
16686
16803
|
get leafId() {
|
|
16687
16804
|
return this.#leafId;
|
|
@@ -16701,6 +16818,7 @@ var SessionHistory = class _SessionHistory {
|
|
|
16701
16818
|
};
|
|
16702
16819
|
this.#nodes.push(node);
|
|
16703
16820
|
this.#leafId = node.id;
|
|
16821
|
+
this.#boundNodes();
|
|
16704
16822
|
await this.#persist();
|
|
16705
16823
|
return structuredClone(node);
|
|
16706
16824
|
}
|
|
@@ -16742,6 +16860,13 @@ var SessionHistory = class _SessionHistory {
|
|
|
16742
16860
|
if (node === void 0) throw new Error(`Unknown session tree node: ${id}`);
|
|
16743
16861
|
return node;
|
|
16744
16862
|
}
|
|
16863
|
+
#boundNodes() {
|
|
16864
|
+
const bounded2 = boundedNodes(this.#nodes);
|
|
16865
|
+
this.#nodes.splice(0, this.#nodes.length, ...bounded2);
|
|
16866
|
+
if (this.#leafId !== null && !this.#nodes.some((node) => node.id === this.#leafId)) {
|
|
16867
|
+
this.#leafId = this.#nodes.at(-1)?.id ?? null;
|
|
16868
|
+
}
|
|
16869
|
+
}
|
|
16745
16870
|
async #persist() {
|
|
16746
16871
|
const document = {
|
|
16747
16872
|
version: 1,
|
|
@@ -16756,6 +16881,24 @@ var SessionHistory = class _SessionHistory {
|
|
|
16756
16881
|
await rename8(temporary, this.#path);
|
|
16757
16882
|
}
|
|
16758
16883
|
};
|
|
16884
|
+
function boundedNodes(input) {
|
|
16885
|
+
const recent = input.slice(-MAX_SESSION_HISTORY_NODES);
|
|
16886
|
+
let contextChars = 0;
|
|
16887
|
+
let start = recent.length;
|
|
16888
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
16889
|
+
const node = recent[index];
|
|
16890
|
+
const size = JSON.stringify(node.context).length;
|
|
16891
|
+
if (start < recent.length && contextChars + size > MAX_SESSION_HISTORY_CONTEXT_CHARS) break;
|
|
16892
|
+
contextChars += size;
|
|
16893
|
+
start = index;
|
|
16894
|
+
}
|
|
16895
|
+
const retained = recent.slice(start).map((node) => structuredClone(node));
|
|
16896
|
+
const ids = new Set(retained.map((node) => node.id));
|
|
16897
|
+
for (const node of retained) {
|
|
16898
|
+
if (node.parentId !== null && !ids.has(node.parentId)) node.parentId = null;
|
|
16899
|
+
}
|
|
16900
|
+
return retained;
|
|
16901
|
+
}
|
|
16759
16902
|
function validate(value, sessionId) {
|
|
16760
16903
|
if (value.version !== 1 || value.sessionId !== sessionId || !Array.isArray(value.nodes)) {
|
|
16761
16904
|
throw new Error("Invalid session tree");
|
|
@@ -16774,11 +16917,35 @@ function isCode10(error, code) {
|
|
|
16774
16917
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
16775
16918
|
}
|
|
16776
16919
|
|
|
16920
|
+
// src/utils/memory-restart.ts
|
|
16921
|
+
import { join as join26 } from "path";
|
|
16922
|
+
var MEMORY_RESTART_EXIT_CODE = 75;
|
|
16923
|
+
var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
|
|
16924
|
+
var MEMORY_RESTART_WINDOW_MS = 30 * 60 * 1e3;
|
|
16925
|
+
function memoryRestartMarkerPath(workspace) {
|
|
16926
|
+
return join26(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
|
|
16927
|
+
}
|
|
16928
|
+
function nextMemoryRestartMarker(previous, sessionId, now) {
|
|
16929
|
+
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;
|
|
16930
|
+
return { sessionId, requestedAt: now.toISOString(), attempts: reusable ? previous.attempts + 1 : 1 };
|
|
16931
|
+
}
|
|
16932
|
+
function parseMemoryRestartMarker(json) {
|
|
16933
|
+
try {
|
|
16934
|
+
const value = JSON.parse(json);
|
|
16935
|
+
if (typeof value.sessionId !== "string" || typeof value.requestedAt !== "string" || typeof value.attempts !== "number") {
|
|
16936
|
+
return void 0;
|
|
16937
|
+
}
|
|
16938
|
+
return { sessionId: value.sessionId, requestedAt: value.requestedAt, attempts: value.attempts };
|
|
16939
|
+
} catch {
|
|
16940
|
+
return void 0;
|
|
16941
|
+
}
|
|
16942
|
+
}
|
|
16943
|
+
|
|
16777
16944
|
// src/sleep/organizer.ts
|
|
16778
16945
|
import { constants as constants7 } from "fs";
|
|
16779
16946
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
16780
16947
|
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
|
|
16948
|
+
import { isAbsolute as isAbsolute10, join as join27, relative as relative15, resolve as resolve22, sep as sep15 } from "path";
|
|
16782
16949
|
import { z as z28 } from "zod";
|
|
16783
16950
|
var DateKeySchema = z28.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
16784
16951
|
var ReviewSchema = z28.object({
|
|
@@ -16833,7 +17000,7 @@ var ProjectSleepOrganizer = class {
|
|
|
16833
17000
|
#generate;
|
|
16834
17001
|
constructor(options) {
|
|
16835
17002
|
this.#workspace = resolve22(options.workspace);
|
|
16836
|
-
this.#sleepDirectory =
|
|
17003
|
+
this.#sleepDirectory = join27(this.#workspace, ".flavor", "sleep");
|
|
16837
17004
|
this.#sessions = options.sessions ?? new SessionStore({ workspace: this.#workspace });
|
|
16838
17005
|
this.#generate = options.generate;
|
|
16839
17006
|
}
|
|
@@ -16842,7 +17009,7 @@ var ProjectSleepOrganizer = class {
|
|
|
16842
17009
|
signal.throwIfAborted();
|
|
16843
17010
|
await this.#prepareDirectory();
|
|
16844
17011
|
if (await this.#hasReport(date)) return { status: "exists", date };
|
|
16845
|
-
const lockPath =
|
|
17012
|
+
const lockPath = join27(this.#sleepDirectory, `.${date}.lock`);
|
|
16846
17013
|
let lock;
|
|
16847
17014
|
try {
|
|
16848
17015
|
lock = await open9(lockPath, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
@@ -16877,7 +17044,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16877
17044
|
}
|
|
16878
17045
|
if (review === void 0) throw lastError;
|
|
16879
17046
|
const filename2 = `${date}-${filenameSummary(review.title)}.md`;
|
|
16880
|
-
const path =
|
|
17047
|
+
const path = join27(this.#sleepDirectory, filename2);
|
|
16881
17048
|
const markdown = renderSleepReport(date, review, documents);
|
|
16882
17049
|
await this.#atomicWrite(path, markdown);
|
|
16883
17050
|
return { status: "written", date, path, sessionCount: documents.length };
|
|
@@ -16919,7 +17086,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16919
17086
|
});
|
|
16920
17087
|
}
|
|
16921
17088
|
async #prepareDirectory() {
|
|
16922
|
-
const flavorDirectory =
|
|
17089
|
+
const flavorDirectory = join27(this.#workspace, ".flavor");
|
|
16923
17090
|
await assertNotSymlink(flavorDirectory);
|
|
16924
17091
|
await mkdir18(flavorDirectory, { recursive: true, mode: 448 });
|
|
16925
17092
|
await assertNotSymlink(this.#sleepDirectory);
|
|
@@ -16933,7 +17100,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16933
17100
|
return (await readdir8(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
|
|
16934
17101
|
}
|
|
16935
17102
|
async #atomicWrite(path, content) {
|
|
16936
|
-
const temporary =
|
|
17103
|
+
const temporary = join27(this.#sleepDirectory, `.${randomUUID16()}.tmp`);
|
|
16937
17104
|
let handle;
|
|
16938
17105
|
try {
|
|
16939
17106
|
handle = await open9(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
@@ -19837,12 +20004,12 @@ ${explanation}` : "";
|
|
|
19837
20004
|
// src/desktop/astgraph-service.ts
|
|
19838
20005
|
import { stat as stat10 } from "fs/promises";
|
|
19839
20006
|
import { createRequire as nodeCreateRequire } from "module";
|
|
19840
|
-
import { join as
|
|
20007
|
+
import { join as join28, resolve as resolve26 } from "path";
|
|
19841
20008
|
var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
|
|
19842
20009
|
var DesktopAstGraphService = class {
|
|
19843
20010
|
#path;
|
|
19844
20011
|
constructor(workspace) {
|
|
19845
|
-
this.#path =
|
|
20012
|
+
this.#path = join28(resolve26(workspace), ".flavor", "astgraph", "index.db");
|
|
19846
20013
|
}
|
|
19847
20014
|
async status() {
|
|
19848
20015
|
if ((await stat10(this.#path).catch(() => void 0))?.isFile() !== true) {
|
|
@@ -20111,7 +20278,7 @@ function createTodoWriteTool() {
|
|
|
20111
20278
|
// src/tools/managed.ts
|
|
20112
20279
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
20113
20280
|
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
|
|
20281
|
+
import { dirname as dirname21, join as join29, resolve as resolve27 } from "path";
|
|
20115
20282
|
import { z as z37 } from "zod";
|
|
20116
20283
|
var MANAGED_TOOL_VERSION = 1;
|
|
20117
20284
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
@@ -20154,8 +20321,8 @@ var ManagedToolStore = class {
|
|
|
20154
20321
|
#diagnostics = [];
|
|
20155
20322
|
constructor(options) {
|
|
20156
20323
|
this.#workspace = resolve27(options.workspace);
|
|
20157
|
-
this.#projectRoot =
|
|
20158
|
-
this.#globalRoot =
|
|
20324
|
+
this.#projectRoot = join29(this.#workspace, ".flavor", "tools");
|
|
20325
|
+
this.#globalRoot = join29(resolve27(options.home), ".flavor-code", "tools");
|
|
20159
20326
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
20160
20327
|
}
|
|
20161
20328
|
get diagnostics() {
|
|
@@ -20202,7 +20369,7 @@ var ManagedToolStore = class {
|
|
|
20202
20369
|
version: MANAGED_TOOL_VERSION,
|
|
20203
20370
|
createdAt: this.#now()
|
|
20204
20371
|
});
|
|
20205
|
-
const path =
|
|
20372
|
+
const path = join29(this.directory(input.scope), filename(input.name));
|
|
20206
20373
|
const definition = compileDefinition(record3, input.scope, this.#workspace);
|
|
20207
20374
|
await writeExclusive(path, `${JSON.stringify(record3, void 0, 2)}
|
|
20208
20375
|
`);
|
|
@@ -20227,7 +20394,7 @@ var ManagedToolStore = class {
|
|
|
20227
20394
|
if (!parsed.success) return [];
|
|
20228
20395
|
const matches = this.#matching(parsed.data.name).filter((entry2) => parsed.data.scope === void 0 || entry2.scope === parsed.data.scope);
|
|
20229
20396
|
if (matches.length > 0) return matches.map((entry2) => entry2.path);
|
|
20230
|
-
return [
|
|
20397
|
+
return [join29(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
|
|
20231
20398
|
}
|
|
20232
20399
|
async #loadDirectory(scope2) {
|
|
20233
20400
|
const root = this.directory(scope2);
|
|
@@ -20240,7 +20407,7 @@ var ManagedToolStore = class {
|
|
|
20240
20407
|
return;
|
|
20241
20408
|
}
|
|
20242
20409
|
for (const name of names) {
|
|
20243
|
-
const path =
|
|
20410
|
+
const path = join29(root, name);
|
|
20244
20411
|
try {
|
|
20245
20412
|
const info = await lstat11(path);
|
|
20246
20413
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
|
|
@@ -22558,7 +22725,7 @@ ${total}`;
|
|
|
22558
22725
|
// src/memory/store.ts
|
|
22559
22726
|
import { createHash as createHash17 } from "crypto";
|
|
22560
22727
|
import { readFile as readFile23, rm as rm12 } from "fs/promises";
|
|
22561
|
-
import { resolve as resolve28, join as
|
|
22728
|
+
import { resolve as resolve28, join as join30, relative as relative19, sep as sep19 } from "path";
|
|
22562
22729
|
|
|
22563
22730
|
// src/memory/similarity.ts
|
|
22564
22731
|
function normalizeForSimilarity(value) {
|
|
@@ -22661,9 +22828,9 @@ var MemoryStore = class {
|
|
|
22661
22828
|
if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
|
|
22662
22829
|
if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
|
|
22663
22830
|
this.workspace = resolve28(options.workspace);
|
|
22664
|
-
this.#memoryRoot =
|
|
22665
|
-
this.path =
|
|
22666
|
-
this.#behaviorPath =
|
|
22831
|
+
this.#memoryRoot = join30(this.workspace, ".flavor", "memory");
|
|
22832
|
+
this.path = join30(this.#memoryRoot, "MEMORY.md");
|
|
22833
|
+
this.#behaviorPath = join30(this.#memoryRoot, "behavior.json");
|
|
22667
22834
|
this.#maxEntries = options.maxEntries;
|
|
22668
22835
|
this.#maxEntryChars = options.maxEntryChars;
|
|
22669
22836
|
}
|
|
@@ -23452,7 +23619,7 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
23452
23619
|
|
|
23453
23620
|
// src/ide/client.ts
|
|
23454
23621
|
import { readFile as readFile24, readdir as readdir10, stat as stat11 } from "fs/promises";
|
|
23455
|
-
import { basename as basename7, join as
|
|
23622
|
+
import { basename as basename7, join as join31, relative as relative21, resolve as resolve30, sep as sep21 } from "path";
|
|
23456
23623
|
var FlavorIdeClient = class {
|
|
23457
23624
|
#workspace;
|
|
23458
23625
|
#home;
|
|
@@ -23548,7 +23715,7 @@ var FlavorIdeClient = class {
|
|
|
23548
23715
|
};
|
|
23549
23716
|
if (await this.#healthy(configured)) return configured;
|
|
23550
23717
|
}
|
|
23551
|
-
const directory =
|
|
23718
|
+
const directory = join31(this.#home, ".flavor-code", "ide");
|
|
23552
23719
|
let entries;
|
|
23553
23720
|
try {
|
|
23554
23721
|
entries = (await readdir10(directory)).filter((name) => name.endsWith(".lock"));
|
|
@@ -23556,7 +23723,7 @@ var FlavorIdeClient = class {
|
|
|
23556
23723
|
return void 0;
|
|
23557
23724
|
}
|
|
23558
23725
|
const candidates = (await Promise.all(entries.map(async (name) => {
|
|
23559
|
-
const path =
|
|
23726
|
+
const path = join31(directory, name);
|
|
23560
23727
|
try {
|
|
23561
23728
|
const [contents, info] = await Promise.all([readFile24(path, "utf8"), stat11(path)]);
|
|
23562
23729
|
const lock = parseLockfile(contents);
|
|
@@ -24063,7 +24230,7 @@ function palSocketAddress(options) {
|
|
|
24063
24230
|
import { createHash as createHash20 } from "crypto";
|
|
24064
24231
|
import { Socket as Socket2 } from "net";
|
|
24065
24232
|
import { tmpdir as tmpdir3 } from "os";
|
|
24066
|
-
import { join as
|
|
24233
|
+
import { join as join33 } from "path";
|
|
24067
24234
|
import { spawn as spawn7 } from "child_process";
|
|
24068
24235
|
|
|
24069
24236
|
// src/pals/broker.ts
|
|
@@ -24076,16 +24243,16 @@ import { dirname as dirname23 } from "path";
|
|
|
24076
24243
|
import { randomBytes as randomBytes3, randomUUID as randomUUID21 } from "crypto";
|
|
24077
24244
|
import { chmod as chmod2, link as link2, mkdir as mkdir21, open as open12, readFile as readFile25, unlink as unlink4 } from "fs/promises";
|
|
24078
24245
|
import { homedir } from "os";
|
|
24079
|
-
import { join as
|
|
24246
|
+
import { join as join32 } from "path";
|
|
24080
24247
|
var PAL_AUTH_TOKEN_BYTES = 32;
|
|
24081
24248
|
var TOKEN_PATTERN = /^[a-f0-9]{64}$/;
|
|
24082
24249
|
async function loadOrCreatePalAuthToken(options = {}) {
|
|
24083
|
-
const directory =
|
|
24084
|
-
const tokenPath =
|
|
24250
|
+
const directory = join32(options.home ?? homedir(), ".flavor-code", "pals");
|
|
24251
|
+
const tokenPath = join32(directory, "auth-token");
|
|
24085
24252
|
await mkdir21(directory, { recursive: true, mode: 448 });
|
|
24086
24253
|
await chmod2(directory, 448);
|
|
24087
24254
|
const token = (options.random ?? randomBytes3)(PAL_AUTH_TOKEN_BYTES).toString("hex");
|
|
24088
|
-
const temporaryPath =
|
|
24255
|
+
const temporaryPath = join32(directory, `.auth-token-${process.pid}-${randomUUID21()}.tmp`);
|
|
24089
24256
|
let handle;
|
|
24090
24257
|
try {
|
|
24091
24258
|
handle = await open12(temporaryPath, "wx", 384);
|
|
@@ -24215,7 +24382,7 @@ function canConnect(address) {
|
|
|
24215
24382
|
}
|
|
24216
24383
|
function startupLockPath(address) {
|
|
24217
24384
|
const digest = createHash20("sha256").update(address, "utf8").digest("hex").slice(0, 24);
|
|
24218
|
-
return
|
|
24385
|
+
return join33(tmpdir3(), `flavor-code-pals-${digest}.startup.lock`);
|
|
24219
24386
|
}
|
|
24220
24387
|
async function spawnProductionBroker(address) {
|
|
24221
24388
|
const entry2 = process.argv[1];
|
|
@@ -25151,8 +25318,8 @@ async function createProductionRuntime(options) {
|
|
|
25151
25318
|
const registeredProviders = registration.registered;
|
|
25152
25319
|
let effectiveLlm = registration.effectiveLlm;
|
|
25153
25320
|
const pluginHost = new PluginHost({
|
|
25154
|
-
globalPluginDirs: [
|
|
25155
|
-
projectPluginDirs: [
|
|
25321
|
+
globalPluginDirs: [join34(home, ".flavor-code", "plugins")],
|
|
25322
|
+
projectPluginDirs: [join34(workspace, ".flavor", "plugins")],
|
|
25156
25323
|
config,
|
|
25157
25324
|
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
25158
25325
|
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
@@ -25260,7 +25427,7 @@ async function createProductionRuntime(options) {
|
|
|
25260
25427
|
const address = palSocketAddress({
|
|
25261
25428
|
platform: process.platform,
|
|
25262
25429
|
userScope: home,
|
|
25263
|
-
runtimeDir:
|
|
25430
|
+
runtimeDir: join34(tmpdir4(), `flavor-code-pals-${createHash21("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
25264
25431
|
});
|
|
25265
25432
|
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
25266
25433
|
instanceId: collaboration.instanceId,
|
|
@@ -25371,8 +25538,8 @@ async function createProductionRuntime(options) {
|
|
|
25371
25538
|
diagnostics.push(`MCP servers could not start: ${message(error)}`);
|
|
25372
25539
|
});
|
|
25373
25540
|
const skills = new SkillRegistry({
|
|
25374
|
-
globalRoots: [
|
|
25375
|
-
projectRoots: [
|
|
25541
|
+
globalRoots: [join34(home, ".flavor-code", "skills")],
|
|
25542
|
+
projectRoots: [join34(workspace, ".flavor", "skills"), ...pluginSkillRoots],
|
|
25376
25543
|
authorizeResource: async () => true,
|
|
25377
25544
|
disabledNames: config.skills.disabled
|
|
25378
25545
|
});
|
|
@@ -25380,7 +25547,7 @@ async function createProductionRuntime(options) {
|
|
|
25380
25547
|
skillsReady.catch(() => void 0);
|
|
25381
25548
|
tools.push(createSkillTool(skills), createSkillResourceTool(skills));
|
|
25382
25549
|
if (options.extraTools !== void 0) tools.push(...options.extraTools);
|
|
25383
|
-
const flavor = await optionalText2(
|
|
25550
|
+
const flavor = await optionalText2(join34(workspace, "FLAVOR.md"));
|
|
25384
25551
|
const instructionBaseline = await workspaceInstructions.baseline();
|
|
25385
25552
|
let memoryContext;
|
|
25386
25553
|
if (memoryStore !== void 0) {
|
|
@@ -25961,7 +26128,7 @@ async function createProductionRuntime(options) {
|
|
|
25961
26128
|
evolveService.toolDefinition(),
|
|
25962
26129
|
...mcpTools
|
|
25963
26130
|
];
|
|
25964
|
-
const loopFlavor = await optionalText2(
|
|
26131
|
+
const loopFlavor = await optionalText2(join34(input.workspace, "FLAVOR.md"));
|
|
25965
26132
|
const loopInstructionBaseline = await loopInstructions.baseline();
|
|
25966
26133
|
const loopEnvironment = createPromptEnvironment({
|
|
25967
26134
|
now: /* @__PURE__ */ new Date(),
|
|
@@ -26206,7 +26373,7 @@ async function createProductionRuntime(options) {
|
|
|
26206
26373
|
contextEpoch: harness.main.context.snapshot().epoch?.id ?? "legacy"
|
|
26207
26374
|
});
|
|
26208
26375
|
const turnId = harnessJournal.startTurn(turnConfig, { prompt, initialUserMessage: runOptions?.initialUserMessage });
|
|
26209
|
-
return durableTurn(
|
|
26376
|
+
return monitorMemoryRestart(durableTurn(persistAfter(runMain(
|
|
26210
26377
|
harness,
|
|
26211
26378
|
skills,
|
|
26212
26379
|
prompt,
|
|
@@ -26222,11 +26389,7 @@ async function createProductionRuntime(options) {
|
|
|
26222
26389
|
runOptions?.initialUserMessage,
|
|
26223
26390
|
runOptions?.additionalContext,
|
|
26224
26391
|
ide
|
|
26225
|
-
), persist, () =>
|
|
26226
|
-
prompt,
|
|
26227
|
-
context: harness.main.context.snapshot(),
|
|
26228
|
-
label: `turn: ${prompt.slice(0, 80)}`
|
|
26229
|
-
})), harnessJournal, turnId, turnConfig);
|
|
26392
|
+
), persist), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
|
|
26230
26393
|
},
|
|
26231
26394
|
runSkill: (skill, prompt, signal) => persistAfter(
|
|
26232
26395
|
runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
|
|
@@ -26526,7 +26689,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
26526
26689
|
return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
|
|
26527
26690
|
}
|
|
26528
26691
|
try {
|
|
26529
|
-
const tokenStore = createFileTokenStore(
|
|
26692
|
+
const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
26530
26693
|
const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
|
|
26531
26694
|
await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
|
|
26532
26695
|
const oauth = new OAuthCallbackAuthProvider({
|
|
@@ -26573,7 +26736,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
26573
26736
|
}
|
|
26574
26737
|
},
|
|
26575
26738
|
async logout() {
|
|
26576
|
-
const tokenStore = createFileTokenStore(
|
|
26739
|
+
const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
26577
26740
|
const tokens2 = await tokenStore.load();
|
|
26578
26741
|
const hadCredentials = Object.keys(tokens2).length > 0 || effectiveLlm !== void 0;
|
|
26579
26742
|
if (!hadCredentials) {
|
|
@@ -26936,14 +27099,6 @@ async function* persistAfter(source, persist) {
|
|
|
26936
27099
|
await persist();
|
|
26937
27100
|
}
|
|
26938
27101
|
}
|
|
26939
|
-
async function* persistAndCheckpointAfter(source, persist, checkpoint) {
|
|
26940
|
-
try {
|
|
26941
|
-
for await (const item of source) yield item;
|
|
26942
|
-
await checkpoint();
|
|
26943
|
-
} finally {
|
|
26944
|
-
await persist();
|
|
26945
|
-
}
|
|
26946
|
-
}
|
|
26947
27102
|
async function* persistEach(source, persist) {
|
|
26948
27103
|
try {
|
|
26949
27104
|
for await (const item of source) {
|
|
@@ -26975,6 +27130,31 @@ async function* durableTurn(source, journal, turnId, config) {
|
|
|
26975
27130
|
}
|
|
26976
27131
|
}
|
|
26977
27132
|
}
|
|
27133
|
+
async function* monitorMemoryRestart(source, onRestartNeeded) {
|
|
27134
|
+
let pressure = false;
|
|
27135
|
+
for await (const event of source) {
|
|
27136
|
+
if (event.type === "error" && event.error.code === "memory_pressure") pressure = true;
|
|
27137
|
+
yield event;
|
|
27138
|
+
}
|
|
27139
|
+
if (pressure) onRestartNeeded();
|
|
27140
|
+
}
|
|
27141
|
+
function requestMemoryRestart(workspace, sessionId, emitOutput) {
|
|
27142
|
+
try {
|
|
27143
|
+
const markerPath = memoryRestartMarkerPath(workspace);
|
|
27144
|
+
let previous;
|
|
27145
|
+
try {
|
|
27146
|
+
previous = parseMemoryRestartMarker(readFileSync3(markerPath, "utf8"));
|
|
27147
|
+
} catch {
|
|
27148
|
+
}
|
|
27149
|
+
mkdirSync3(dirname24(markerPath), { recursive: true });
|
|
27150
|
+
writeFileSync(markerPath, `${JSON.stringify(nextMemoryRestartMarker(previous, sessionId, /* @__PURE__ */ new Date()))}
|
|
27151
|
+
`, "utf8");
|
|
27152
|
+
} catch {
|
|
27153
|
+
}
|
|
27154
|
+
process.exitCode = MEMORY_RESTART_EXIT_CODE;
|
|
27155
|
+
emitOutput({ type: "notice", message: "Memory watermark reached; the session is saved and Flavor is restarting with a fresh heap." });
|
|
27156
|
+
emitOutput({ type: "exit" });
|
|
27157
|
+
}
|
|
26978
27158
|
async function runGitCommit(deps, hint, signal) {
|
|
26979
27159
|
if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
|
|
26980
27160
|
let summary2 = await changeSummary(deps.workspace);
|
|
@@ -27068,7 +27248,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
27068
27248
|
const configured = { ...providers };
|
|
27069
27249
|
if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
|
|
27070
27250
|
if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
|
|
27071
|
-
const oauthTokenStore = createFileTokenStore(
|
|
27251
|
+
const oauthTokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
27072
27252
|
const registered = [];
|
|
27073
27253
|
let effectiveLlm;
|
|
27074
27254
|
for (const [name, provider] of Object.entries(configured)) {
|
|
@@ -27501,11 +27681,11 @@ function createMemoryTaskId() {
|
|
|
27501
27681
|
import { createHash as createHash27 } from "crypto";
|
|
27502
27682
|
import { mkdir as mkdir27, readFile as readFile35, readdir as readdir12, rm as rm15, stat as stat15, writeFile as writeFile17 } from "fs/promises";
|
|
27503
27683
|
import { homedir as homedir4 } from "os";
|
|
27504
|
-
import { basename as basename9, join as
|
|
27684
|
+
import { basename as basename9, join as join44, resolve as resolve36 } from "path";
|
|
27505
27685
|
|
|
27506
27686
|
// src/d2c/workflow.ts
|
|
27507
27687
|
import { createHash as createHash22 } from "crypto";
|
|
27508
|
-
import { join as
|
|
27688
|
+
import { join as join35 } from "path";
|
|
27509
27689
|
import { z as z42 } from "zod";
|
|
27510
27690
|
|
|
27511
27691
|
// src/d2c/workflow-shared.ts
|
|
@@ -27651,7 +27831,7 @@ function reviewsFor(report, previous) {
|
|
|
27651
27831
|
function implementationDirectory(report) {
|
|
27652
27832
|
const source = report.implementation.source;
|
|
27653
27833
|
if (!/^https?:\/\//i.test(source)) return source;
|
|
27654
|
-
return
|
|
27834
|
+
return join35("src", "d2c-output", report.task);
|
|
27655
27835
|
}
|
|
27656
27836
|
function createWorkflow(report, framework) {
|
|
27657
27837
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -27775,7 +27955,7 @@ function applyQualityIssueDecision(workflow, issueId2, decision) {
|
|
|
27775
27955
|
};
|
|
27776
27956
|
}
|
|
27777
27957
|
function workflowPath(workspace, task) {
|
|
27778
|
-
return
|
|
27958
|
+
return join35(taskDir(workspace, task), "workflow.json");
|
|
27779
27959
|
}
|
|
27780
27960
|
async function readWorkflow(workspace, task) {
|
|
27781
27961
|
return (await readRecoverableFile(workflowPath(workspace, task), decodeWorkflow))?.value;
|
|
@@ -28029,7 +28209,7 @@ function confirmApiMapping(mapping, operationKey, operations) {
|
|
|
28029
28209
|
|
|
28030
28210
|
// src/d2c/modules.ts
|
|
28031
28211
|
import { readFile as readFile28 } from "fs/promises";
|
|
28032
|
-
import { isAbsolute as isAbsolute15, join as
|
|
28212
|
+
import { isAbsolute as isAbsolute15, join as join36, normalize as normalize2, posix, sep as sep24, win32 as win322 } from "path";
|
|
28033
28213
|
import { z as z43 } from "zod";
|
|
28034
28214
|
var ModuleSchema = z43.object({
|
|
28035
28215
|
id: z43.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
|
|
@@ -28048,13 +28228,13 @@ function safeSource(path) {
|
|
|
28048
28228
|
}
|
|
28049
28229
|
function d2cOutputDirectory(workspace, task) {
|
|
28050
28230
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
|
|
28051
|
-
return
|
|
28231
|
+
return join36(workspace, "src", "d2c-output", task);
|
|
28052
28232
|
}
|
|
28053
28233
|
async function readD2cModules(workspace, task) {
|
|
28054
28234
|
const project = d2cOutputDirectory(workspace, task);
|
|
28055
28235
|
let raw;
|
|
28056
28236
|
try {
|
|
28057
|
-
raw = await readFile28(
|
|
28237
|
+
raw = await readFile28(join36(project, "d2c.modules.json"), "utf8");
|
|
28058
28238
|
} catch {
|
|
28059
28239
|
return [{ id: "page", label: task, sourceFiles: ["src/App.vue", "src/App.jsx"], keywords: [task] }];
|
|
28060
28240
|
}
|
|
@@ -28075,7 +28255,7 @@ async function readD2cModules(workspace, task) {
|
|
|
28075
28255
|
|
|
28076
28256
|
// src/d2c/integration.ts
|
|
28077
28257
|
import { mkdir as mkdir24, readFile as readFile29, writeFile as writeFile13 } from "fs/promises";
|
|
28078
|
-
import { join as
|
|
28258
|
+
import { join as join37 } from "path";
|
|
28079
28259
|
function record2(value) {
|
|
28080
28260
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
28081
28261
|
}
|
|
@@ -28211,7 +28391,7 @@ async def reset_acceptance(request: Request):
|
|
|
28211
28391
|
}
|
|
28212
28392
|
async function generateIntegrationArtifacts(projectDir, document, mappings, options = {}) {
|
|
28213
28393
|
if (mappings.some((mapping) => mapping.status === "needs-confirmation")) throw new Error("Confirm every uncertain OpenAPI mapping before generating integration code");
|
|
28214
|
-
const packagePath =
|
|
28394
|
+
const packagePath = join37(projectDir, "package.json");
|
|
28215
28395
|
let pkg;
|
|
28216
28396
|
try {
|
|
28217
28397
|
pkg = JSON.parse(await readFile29(packagePath, "utf8"));
|
|
@@ -28221,9 +28401,9 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
28221
28401
|
const scripts = options.pythonServer ? { ...record2(pkg.scripts) ?? {} } : { ...record2(pkg.scripts) ?? {}, mock: "node mock/server.mjs" };
|
|
28222
28402
|
const dependencies2 = options.pythonServer ? { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9" } : { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9", express: "^5.2.1" };
|
|
28223
28403
|
const selected = [...new Set(mappings.map((mapping) => mapping.operationKey))].map((key) => document.operations.find((operation) => operation.id === key)).filter((operation) => operation !== void 0);
|
|
28224
|
-
const apiDir =
|
|
28225
|
-
const mockDir =
|
|
28226
|
-
const serverDir =
|
|
28404
|
+
const apiDir = join37(projectDir, "src", "api");
|
|
28405
|
+
const mockDir = join37(projectDir, "mock");
|
|
28406
|
+
const serverDir = join37(projectDir, "server");
|
|
28227
28407
|
await Promise.all([
|
|
28228
28408
|
mkdir24(apiDir, { recursive: true }),
|
|
28229
28409
|
...options.pythonServer ? [] : [mkdir24(mockDir, { recursive: true })],
|
|
@@ -28233,23 +28413,23 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
28233
28413
|
if (options.pythonServer) files.push("server/main.py", "server/requirements.txt", "server/README.md", "server/flavor-runtime.json");
|
|
28234
28414
|
else files.push("mock/server.mjs");
|
|
28235
28415
|
await Promise.all([
|
|
28236
|
-
writeFile13(
|
|
28416
|
+
writeFile13(join37(apiDir, "http.js"), `import axios from "axios";
|
|
28237
28417
|
|
|
28238
28418
|
export const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || ${JSON.stringify(document.baseUrl ?? "")}, timeout: 15000 });
|
|
28239
28419
|
|
|
28240
28420
|
http.interceptors.response.use((response) => response, (error) => Promise.reject({ name: "ApiError", status: error.response?.status, message: error.response?.data?.message || error.message, cause: error }));
|
|
28241
28421
|
`),
|
|
28242
|
-
writeFile13(
|
|
28243
|
-
writeFile13(
|
|
28422
|
+
writeFile13(join37(apiDir, "d2c-api.js"), apiModule(selected)),
|
|
28423
|
+
writeFile13(join37(apiDir, "d2c-bindings.json"), `${JSON.stringify({ schema: 1, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), mappings }, null, 2)}
|
|
28244
28424
|
`),
|
|
28245
|
-
...options.pythonServer ? [] : [writeFile13(
|
|
28425
|
+
...options.pythonServer ? [] : [writeFile13(join37(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
|
|
28246
28426
|
writeFile13(packagePath, `${JSON.stringify({ ...pkg, scripts, dependencies: dependencies2 }, null, 2)}
|
|
28247
28427
|
`),
|
|
28248
28428
|
...options.pythonServer ? [
|
|
28249
|
-
writeFile13(
|
|
28250
|
-
writeFile13(
|
|
28251
|
-
writeFile13(
|
|
28252
|
-
writeFile13(
|
|
28429
|
+
writeFile13(join37(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
|
|
28430
|
+
writeFile13(join37(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
|
|
28431
|
+
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"),
|
|
28432
|
+
writeFile13(join37(serverDir, "flavor-runtime.json"), `${JSON.stringify({
|
|
28253
28433
|
schema: 1,
|
|
28254
28434
|
kind: "python-fastapi",
|
|
28255
28435
|
cwd: "server",
|
|
@@ -28270,7 +28450,7 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
|
|
|
28270
28450
|
import { spawn as spawn8 } from "child_process";
|
|
28271
28451
|
import { access as access3, readFile as readFile30 } from "fs/promises";
|
|
28272
28452
|
import { createServer as createServer5 } from "net";
|
|
28273
|
-
import { join as
|
|
28453
|
+
import { join as join38 } from "path";
|
|
28274
28454
|
async function exists3(path) {
|
|
28275
28455
|
try {
|
|
28276
28456
|
await access3(path);
|
|
@@ -28378,7 +28558,7 @@ ${output.trim()}` : ""}`);
|
|
|
28378
28558
|
}
|
|
28379
28559
|
}
|
|
28380
28560
|
async function runPythonBackend(projectDir, options) {
|
|
28381
|
-
const serverDir =
|
|
28561
|
+
const serverDir = join38(projectDir, "server");
|
|
28382
28562
|
if (options.installDependencies !== false) await installPython(serverDir, options.signal);
|
|
28383
28563
|
const selectedPort = await port();
|
|
28384
28564
|
const url = `http://127.0.0.1:${selectedPort}`;
|
|
@@ -28424,7 +28604,7 @@ ${managed.output().slice(-4e3)}`);
|
|
|
28424
28604
|
};
|
|
28425
28605
|
}
|
|
28426
28606
|
async function runD2cMockServer(projectDir, options = {}) {
|
|
28427
|
-
const runtimePath =
|
|
28607
|
+
const runtimePath = join38(projectDir, "server", "flavor-runtime.json");
|
|
28428
28608
|
if (await exists3(runtimePath)) {
|
|
28429
28609
|
let runtime;
|
|
28430
28610
|
try {
|
|
@@ -28435,9 +28615,9 @@ async function runD2cMockServer(projectDir, options = {}) {
|
|
|
28435
28615
|
if (runtime.schema !== 1 || runtime.kind !== "python-fastapi") throw new Error("Unsupported E2E backend runtime manifest");
|
|
28436
28616
|
return runPythonBackend(projectDir, options);
|
|
28437
28617
|
}
|
|
28438
|
-
const serverPath =
|
|
28618
|
+
const serverPath = join38(projectDir, "mock", "server.mjs");
|
|
28439
28619
|
if (!await exists3(serverPath)) throw new Error(`D2C mock server is not generated at ${serverPath}`);
|
|
28440
|
-
if (options.installDependencies === false && !await exists3(
|
|
28620
|
+
if (options.installDependencies === false && !await exists3(join38(projectDir, "node_modules", "express", "package.json"))) {
|
|
28441
28621
|
throw new Error("D2C mock server dependencies are not installed");
|
|
28442
28622
|
}
|
|
28443
28623
|
if (options.installDependencies !== false) await install(projectDir, options.signal);
|
|
@@ -28480,7 +28660,7 @@ ${managed.output().slice(-4e3)}`);
|
|
|
28480
28660
|
// src/d2c/product.ts
|
|
28481
28661
|
import { createHash as createHash24, randomUUID as randomUUID27 } from "crypto";
|
|
28482
28662
|
import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile32, rename as rename13, stat as stat13, unlink as unlink7, writeFile as writeFile15 } from "fs/promises";
|
|
28483
|
-
import { join as
|
|
28663
|
+
import { join as join39 } from "path";
|
|
28484
28664
|
import { z as z44 } from "zod";
|
|
28485
28665
|
|
|
28486
28666
|
// src/e2e/prd-governance.ts
|
|
@@ -28635,13 +28815,13 @@ var PlanSchema2 = z44.object({
|
|
|
28635
28815
|
}).strict();
|
|
28636
28816
|
function d2cProductDirectory(workspace, task) {
|
|
28637
28817
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
|
|
28638
|
-
return
|
|
28818
|
+
return join39(taskDir(workspace, task), "product");
|
|
28639
28819
|
}
|
|
28640
28820
|
function d2cProductPrototypeDirectory(workspace, task) {
|
|
28641
|
-
return
|
|
28821
|
+
return join39(d2cProductDirectory(workspace, task), "prototype");
|
|
28642
28822
|
}
|
|
28643
28823
|
function planPath(workspace, task) {
|
|
28644
|
-
return
|
|
28824
|
+
return join39(taskDir(workspace, task), "product-plan.json");
|
|
28645
28825
|
}
|
|
28646
28826
|
function resolveD2cProductTechnology(requirement, fallbackFramework = "vue") {
|
|
28647
28827
|
const frontendCandidates = [
|
|
@@ -28824,16 +29004,16 @@ async function readD2cProductPlan(workspace, task) {
|
|
|
28824
29004
|
}
|
|
28825
29005
|
async function discoverD2cProductArtifacts(workspace, plan) {
|
|
28826
29006
|
const product = d2cProductDirectory(workspace, plan.task);
|
|
28827
|
-
const prdUpdatedAt = await fileTimestamp(
|
|
28828
|
-
const prdHash = await fileHash(
|
|
28829
|
-
const prototypeUpdatedAt = await fileTimestamp(
|
|
28830
|
-
const prototypeHash = await fileHash(
|
|
28831
|
-
const expectedManifest =
|
|
29007
|
+
const prdUpdatedAt = await fileTimestamp(join39(product, "prd.md"));
|
|
29008
|
+
const prdHash = await fileHash(join39(product, "prd.md"));
|
|
29009
|
+
const prototypeUpdatedAt = await fileTimestamp(join39(product, "prototype", "index.html"));
|
|
29010
|
+
const prototypeHash = await fileHash(join39(product, "prototype", "index.html"));
|
|
29011
|
+
const expectedManifest = join39(product, "prototype", "interaction-manifest.json");
|
|
28832
29012
|
let manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
28833
29013
|
if (manifestUpdatedAt === void 0) {
|
|
28834
|
-
const misplacedManifest =
|
|
29014
|
+
const misplacedManifest = join39(product, "interaction-manifest.json");
|
|
28835
29015
|
if (await fileTimestamp(misplacedManifest) !== void 0) {
|
|
28836
|
-
await mkdir25(
|
|
29016
|
+
await mkdir25(join39(product, "prototype"), { recursive: true });
|
|
28837
29017
|
await copyFile2(misplacedManifest, expectedManifest);
|
|
28838
29018
|
manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
28839
29019
|
}
|
|
@@ -28866,8 +29046,8 @@ async function readD2cProductPlanView(workspace, task) {
|
|
|
28866
29046
|
if (stored === void 0) return void 0;
|
|
28867
29047
|
const plan = await discoverD2cProductArtifacts(workspace, stored);
|
|
28868
29048
|
if (plan.revision !== stored.revision) await writeD2cProductPlan(workspace, plan);
|
|
28869
|
-
const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(
|
|
28870
|
-
const manifestPath =
|
|
29049
|
+
const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(join39(taskDir(workspace, task), plan.prd.path), "utf8");
|
|
29050
|
+
const manifestPath = join39(d2cProductDirectory(workspace, task), "prototype", "interaction-manifest.json");
|
|
28871
29051
|
const validationError = plan.phase === "design-generating" && await fileTimestamp(manifestPath) !== void 0 ? await validateAndNormalizePrototypeManifest(manifestPath) : void 0;
|
|
28872
29052
|
return {
|
|
28873
29053
|
plan,
|
|
@@ -29026,7 +29206,7 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
|
|
|
29026
29206
|
|
|
29027
29207
|
// src/e2e/delivery-run.ts
|
|
29028
29208
|
import { createHash as createHash26 } from "crypto";
|
|
29029
|
-
import { join as
|
|
29209
|
+
import { join as join40 } from "path";
|
|
29030
29210
|
import { z as z45 } from "zod";
|
|
29031
29211
|
var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
|
|
29032
29212
|
var ArtifactSchema = z45.object({
|
|
@@ -29143,7 +29323,7 @@ function completeDeliveryNode(run, node, outputs, now = /* @__PURE__ */ new Date
|
|
|
29143
29323
|
return { ...run, nodes, updatedAt: timestamp2 };
|
|
29144
29324
|
}
|
|
29145
29325
|
function deliveryRunPath(workspace, task) {
|
|
29146
|
-
return
|
|
29326
|
+
return join40(taskDir(workspace, task), "delivery-run.json");
|
|
29147
29327
|
}
|
|
29148
29328
|
function decodeDeliveryRun(raw) {
|
|
29149
29329
|
return DeliveryRunSchema.parse(JSON.parse(raw));
|
|
@@ -29186,7 +29366,7 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
|
|
|
29186
29366
|
// src/skills/manager.ts
|
|
29187
29367
|
import { lstat as lstat13, mkdir as mkdir26, readFile as readFile34, readdir as readdir11, realpath as realpath13, rm as rm14, writeFile as writeFile16 } from "fs/promises";
|
|
29188
29368
|
import { homedir as homedir3 } from "os";
|
|
29189
|
-
import { isAbsolute as isAbsolute16, join as
|
|
29369
|
+
import { isAbsolute as isAbsolute16, join as join41, relative as relative24, resolve as resolve34, sep as sep25 } from "path";
|
|
29190
29370
|
import { stringify } from "yaml";
|
|
29191
29371
|
var SkillManager = class {
|
|
29192
29372
|
#workspace;
|
|
@@ -29209,7 +29389,7 @@ var SkillManager = class {
|
|
|
29209
29389
|
async create(draft) {
|
|
29210
29390
|
const value = parseDraft(draft);
|
|
29211
29391
|
const projectRoot = this.#projectRoot();
|
|
29212
|
-
const directory =
|
|
29392
|
+
const directory = join41(projectRoot, value.name);
|
|
29213
29393
|
await mkdir26(projectRoot, { recursive: true, mode: 448 });
|
|
29214
29394
|
try {
|
|
29215
29395
|
await mkdir26(directory, { mode: 448 });
|
|
@@ -29218,7 +29398,7 @@ var SkillManager = class {
|
|
|
29218
29398
|
throw error;
|
|
29219
29399
|
}
|
|
29220
29400
|
try {
|
|
29221
|
-
await writeFile16(
|
|
29401
|
+
await writeFile16(join41(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
29222
29402
|
await this.#persistEnabled(value.name, true);
|
|
29223
29403
|
return this.get(value.name);
|
|
29224
29404
|
} catch (error) {
|
|
@@ -29236,7 +29416,7 @@ var SkillManager = class {
|
|
|
29236
29416
|
throw error;
|
|
29237
29417
|
});
|
|
29238
29418
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`Project skill path is not a real directory: ${validName}`);
|
|
29239
|
-
const skillFile =
|
|
29419
|
+
const skillFile = join41(directory, "SKILL.md");
|
|
29240
29420
|
const fileInfo = await lstat13(skillFile);
|
|
29241
29421
|
if (!fileInfo.isFile() || fileInfo.isSymbolicLink()) throw new Error(`Project skill file is not a real file: ${validName}`);
|
|
29242
29422
|
await writeFile16(skillFile, renderSkill(value), { encoding: "utf8", mode: 384 });
|
|
@@ -29268,12 +29448,12 @@ var SkillManager = class {
|
|
|
29268
29448
|
async #registry() {
|
|
29269
29449
|
const loaded = await loadConfig({ cwd: this.#workspace, home: this.#home });
|
|
29270
29450
|
const [globalPluginRoots, projectPluginRoots] = await Promise.all([
|
|
29271
|
-
discoverPluginSkillRoots(
|
|
29272
|
-
discoverPluginSkillRoots(
|
|
29451
|
+
discoverPluginSkillRoots(join41(this.#home, ".flavor-code", "plugins")),
|
|
29452
|
+
discoverPluginSkillRoots(join41(this.#workspace, ".flavor", "plugins"))
|
|
29273
29453
|
]);
|
|
29274
29454
|
return {
|
|
29275
29455
|
registry: new SkillRegistry({
|
|
29276
|
-
globalRoots: [
|
|
29456
|
+
globalRoots: [join41(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
|
|
29277
29457
|
projectRoots: [this.#projectRoot(), ...projectPluginRoots]
|
|
29278
29458
|
}),
|
|
29279
29459
|
disabled: new Set(loaded.config.skills.disabled)
|
|
@@ -29289,10 +29469,10 @@ var SkillManager = class {
|
|
|
29289
29469
|
};
|
|
29290
29470
|
}
|
|
29291
29471
|
#projectRoot() {
|
|
29292
|
-
return
|
|
29472
|
+
return join41(this.#workspace, ".flavor", "skills");
|
|
29293
29473
|
}
|
|
29294
29474
|
#projectSkillDirectory(name) {
|
|
29295
|
-
return
|
|
29475
|
+
return join41(this.#projectRoot(), name);
|
|
29296
29476
|
}
|
|
29297
29477
|
};
|
|
29298
29478
|
function parseName(value) {
|
|
@@ -29333,9 +29513,9 @@ async function discoverPluginSkillRoots(directory) {
|
|
|
29333
29513
|
const roots = [];
|
|
29334
29514
|
for (const entry2 of entries) {
|
|
29335
29515
|
if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue;
|
|
29336
|
-
const pluginRoot = await realpath13(
|
|
29516
|
+
const pluginRoot = await realpath13(join41(directory, entry2.name));
|
|
29337
29517
|
try {
|
|
29338
|
-
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(
|
|
29518
|
+
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(join41(pluginRoot, "flavor-plugin.json"), "utf8")));
|
|
29339
29519
|
for (const contribution of manifest.contributes.skillRoots) {
|
|
29340
29520
|
const candidate = resolve34(pluginRoot, contribution.path);
|
|
29341
29521
|
const info = await lstat13(candidate);
|
|
@@ -29410,11 +29590,11 @@ async function createProjectMemoryManager(options) {
|
|
|
29410
29590
|
}
|
|
29411
29591
|
|
|
29412
29592
|
// src/mcp/config-manager.ts
|
|
29413
|
-
import { join as
|
|
29593
|
+
import { join as join42 } from "path";
|
|
29414
29594
|
var ProjectMcpConfigManager = class {
|
|
29415
29595
|
path;
|
|
29416
29596
|
constructor(workspace) {
|
|
29417
|
-
this.path =
|
|
29597
|
+
this.path = join42(workspace, ".flavor", "flavor.json");
|
|
29418
29598
|
}
|
|
29419
29599
|
async list() {
|
|
29420
29600
|
const document = await readRecoverableFile(this.path, (raw) => parseDocument2(this.path, raw));
|
|
@@ -29511,7 +29691,7 @@ function isRecord7(value) {
|
|
|
29511
29691
|
}
|
|
29512
29692
|
|
|
29513
29693
|
// src/desktop/model-config.ts
|
|
29514
|
-
import { join as
|
|
29694
|
+
import { join as join43 } from "path";
|
|
29515
29695
|
var DEFAULT_DESKTOP_MODELS = [
|
|
29516
29696
|
{
|
|
29517
29697
|
id: "anthropic:deepseek-v4-pro",
|
|
@@ -29532,7 +29712,7 @@ var DEFAULT_DESKTOP_MODELS = [
|
|
|
29532
29712
|
];
|
|
29533
29713
|
async function loadDesktopModels(workspace, home) {
|
|
29534
29714
|
try {
|
|
29535
|
-
const tokens2 = await createFileTokenStore(
|
|
29715
|
+
const tokens2 = await createFileTokenStore(join43(home, ".flavor-code", "auth.json")).load();
|
|
29536
29716
|
const pkceModels = [];
|
|
29537
29717
|
const knownPkce = /* @__PURE__ */ new Set();
|
|
29538
29718
|
for (const token of Object.values(tokens2)) {
|
|
@@ -29677,33 +29857,33 @@ function pngDataUrl(png) {
|
|
|
29677
29857
|
}
|
|
29678
29858
|
async function detectD2cFramework(workspace, task) {
|
|
29679
29859
|
try {
|
|
29680
|
-
const pkg = JSON.parse(await readFile35(
|
|
29860
|
+
const pkg = JSON.parse(await readFile35(join44(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
|
|
29681
29861
|
return pkg.dependencies?.react === void 0 ? "vue" : "react";
|
|
29682
29862
|
} catch {
|
|
29683
29863
|
return "vue";
|
|
29684
29864
|
}
|
|
29685
29865
|
}
|
|
29686
29866
|
function integrationDirectory(workspace, task) {
|
|
29687
|
-
return
|
|
29867
|
+
return join44(workspace, ".flavor", "d2c", task, "integration");
|
|
29688
29868
|
}
|
|
29689
29869
|
var D2C_BACKEND_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".cfg", ".env", ".ini", ".js", ".json", ".lock", ".mjs", ".py", ".toml", ".txt", ".yaml", ".yml"]);
|
|
29690
29870
|
var D2C_BACKEND_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".venv", "__pycache__", "data", "node_modules", "venv"]);
|
|
29691
29871
|
async function d2cBackendSourceFingerprint(project) {
|
|
29692
|
-
const runtimeManifest =
|
|
29872
|
+
const runtimeManifest = join44(project, "server", "flavor-runtime.json");
|
|
29693
29873
|
const realBackend = (await stat15(runtimeManifest).catch(() => void 0))?.isFile() === true;
|
|
29694
|
-
const root = realBackend ?
|
|
29874
|
+
const root = realBackend ? join44(project, "server") : join44(project, "mock");
|
|
29695
29875
|
if ((await stat15(root).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
29696
29876
|
const files = [];
|
|
29697
29877
|
const visit = async (directory) => {
|
|
29698
29878
|
const entries = await readdir12(directory, { withFileTypes: true });
|
|
29699
29879
|
for (const entry2 of entries) {
|
|
29700
29880
|
if (entry2.isDirectory()) {
|
|
29701
|
-
if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(
|
|
29881
|
+
if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(join44(directory, entry2.name));
|
|
29702
29882
|
continue;
|
|
29703
29883
|
}
|
|
29704
29884
|
if (!entry2.isFile()) continue;
|
|
29705
29885
|
const extension2 = entry2.name.includes(".") ? `.${entry2.name.split(".").pop().toLowerCase()}` : "";
|
|
29706
|
-
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(
|
|
29886
|
+
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join44(directory, entry2.name));
|
|
29707
29887
|
}
|
|
29708
29888
|
};
|
|
29709
29889
|
await visit(root);
|
|
@@ -30257,7 +30437,7 @@ var DesktopRuntimeController = class {
|
|
|
30257
30437
|
if (workflow === void 0 || !reviewProgress(workflow).complete || workflow.stage === "visual-review") {
|
|
30258
30438
|
throw new Error("Complete visual review before importing OpenAPI");
|
|
30259
30439
|
}
|
|
30260
|
-
const moduleManifest =
|
|
30440
|
+
const moduleManifest = join44(d2cOutputDirectory(workspace, task), "d2c.modules.json");
|
|
30261
30441
|
const moduleManifestInfo = await stat15(moduleManifest).catch(() => void 0);
|
|
30262
30442
|
if (moduleManifestInfo === void 0 || !moduleManifestInfo.isFile()) {
|
|
30263
30443
|
throw new Error("D2C implementation must write d2c.modules.json before API mapping; ask the agent to split the project into modules first");
|
|
@@ -30270,8 +30450,8 @@ var DesktopRuntimeController = class {
|
|
|
30270
30450
|
await mkdir27(dir, { recursive: true });
|
|
30271
30451
|
const hash = createHash27("sha256").update(raw).digest("hex");
|
|
30272
30452
|
await Promise.all([
|
|
30273
|
-
writeFile17(
|
|
30274
|
-
writeFile17(
|
|
30453
|
+
writeFile17(join44(dir, "swagger.json"), raw),
|
|
30454
|
+
writeFile17(join44(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
|
|
30275
30455
|
`)
|
|
30276
30456
|
]);
|
|
30277
30457
|
const next = {
|
|
@@ -30306,7 +30486,7 @@ var DesktopRuntimeController = class {
|
|
|
30306
30486
|
let workflow = await readWorkflow(workspace, task);
|
|
30307
30487
|
if (workflow?.openapi === void 0 && workflow !== void 0 && reviewProgress(workflow).complete && await readD2cProductPlanView(workspace, task) !== void 0) {
|
|
30308
30488
|
const modules = await readD2cModules(workspace, task);
|
|
30309
|
-
const productOpenApiPath =
|
|
30489
|
+
const productOpenApiPath = join44(taskDir(workspace, task), "product", "openapi.json");
|
|
30310
30490
|
const authored = await readFile35(productOpenApiPath, "utf8").catch(() => void 0);
|
|
30311
30491
|
const generated = authored === void 0;
|
|
30312
30492
|
const prepared = await this.#storeD2cOpenApi(
|
|
@@ -30319,7 +30499,7 @@ var DesktopRuntimeController = class {
|
|
|
30319
30499
|
return prepared;
|
|
30320
30500
|
}
|
|
30321
30501
|
if (workflow?.openapi === void 0 || workflow.mappings === void 0) return void 0;
|
|
30322
|
-
const document = JSON.parse(await readFile35(
|
|
30502
|
+
const document = JSON.parse(await readFile35(join44(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
|
|
30323
30503
|
return { workflow, document, mappings: workflow.mappings };
|
|
30324
30504
|
}
|
|
30325
30505
|
async confirmD2cMapping(task, moduleId, operationKey) {
|
|
@@ -30386,7 +30566,7 @@ var DesktopRuntimeController = class {
|
|
|
30386
30566
|
].join("\n");
|
|
30387
30567
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30388
30568
|
if (delivery?.nodes.api.status === "running") {
|
|
30389
|
-
const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile35(
|
|
30569
|
+
const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile35(join44(project, file)))));
|
|
30390
30570
|
const completed = await updateDeliveryRun(
|
|
30391
30571
|
workspace,
|
|
30392
30572
|
task,
|
|
@@ -30413,7 +30593,7 @@ var DesktopRuntimeController = class {
|
|
|
30413
30593
|
try {
|
|
30414
30594
|
const fingerprint2 = await d2cBackendSourceFingerprint(project);
|
|
30415
30595
|
if (fingerprint2 !== void 0) this.#d2cMockSourceFingerprints.set(task, fingerprint2);
|
|
30416
|
-
const environmentPath =
|
|
30596
|
+
const environmentPath = join44(project, ".env.local");
|
|
30417
30597
|
const existingEnvironment = await readFile35(environmentPath, "utf8").catch(() => "");
|
|
30418
30598
|
const environment = `${existingEnvironment.split(/\r?\n/).filter((line) => !/^\s*VITE_API_BASE_URL\s*=/.test(line)).join("\n").trim()}
|
|
30419
30599
|
VITE_API_BASE_URL=${running.url}
|
|
@@ -30494,7 +30674,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30494
30674
|
const readyPreview = this.#d2cPreviews.get(task);
|
|
30495
30675
|
const readyMock = this.#d2cMocks.get(task);
|
|
30496
30676
|
if (readyPreview === void 0 || readyMock === void 0) throw new Error("D2C preview or backend service is unavailable after synchronization");
|
|
30497
|
-
const manifestPath =
|
|
30677
|
+
const manifestPath = join44(taskDir(workspace, task), "design", "interaction-manifest.json");
|
|
30498
30678
|
const manifest = parseInteractionManifest(await readFile35(manifestPath, "utf8"));
|
|
30499
30679
|
const baseline = await this.#verifyAcceptanceBaseline(workspace, task);
|
|
30500
30680
|
const coverage = baseline === void 0 ? void 0 : verifyRequirementCoverage(baseline.approvedPrd.criteria, manifest);
|
|
@@ -30506,7 +30686,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30506
30686
|
await mkdir27(planDir, { recursive: true });
|
|
30507
30687
|
try {
|
|
30508
30688
|
const observations = await this.#observeD2cPages(manifest, readyPreview.url);
|
|
30509
|
-
const prd = await readFile35(
|
|
30689
|
+
const prd = await readFile35(join44(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
|
|
30510
30690
|
const apiContext = JSON.stringify({ openapi: workflow.openapi, mappings: workflow.mappings }, null, 2);
|
|
30511
30691
|
const plan = await this.#d2cJudge.planInteractions({ task, seed: manifest, observations, ...prd === void 0 ? {} : { prd }, apiContext });
|
|
30512
30692
|
executableManifest = mergeInteractionManifests(manifest, plan.manifest);
|
|
@@ -30517,9 +30697,9 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30517
30697
|
plannedScenarios: executableManifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
30518
30698
|
pageAnalyses: plan.pageAnalyses
|
|
30519
30699
|
};
|
|
30520
|
-
await writeFile17(
|
|
30700
|
+
await writeFile17(join44(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
|
|
30521
30701
|
`, { encoding: "utf8", mode: 384 });
|
|
30522
|
-
await rm15(
|
|
30702
|
+
await rm15(join44(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
|
|
30523
30703
|
} catch (cause) {
|
|
30524
30704
|
const detail = message(cause);
|
|
30525
30705
|
review = {
|
|
@@ -30527,8 +30707,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30527
30707
|
plannedScenarios: manifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
30528
30708
|
warning: `\u591A\u6A21\u6001\u81EA\u4E3B\u89C4\u5212\u5931\u8D25\uFF0C\u5DF2\u7EE7\u7EED\u6267\u884C\u5DF2\u786E\u8BA4\u7684\u8BBE\u8BA1\u5951\u7EA6\uFF1A${detail}`
|
|
30529
30709
|
};
|
|
30530
|
-
await rm15(
|
|
30531
|
-
await writeFile17(
|
|
30710
|
+
await rm15(join44(planDir, "autonomous-interaction-plan.json"), { force: true });
|
|
30711
|
+
await writeFile17(join44(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
|
|
30532
30712
|
schema: 1,
|
|
30533
30713
|
stage: "planning",
|
|
30534
30714
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -30542,13 +30722,13 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30542
30722
|
let result = { ...rawResult, evidenceMode: review.mode };
|
|
30543
30723
|
if (coverage !== void 0) {
|
|
30544
30724
|
const evidence = createAcceptanceEvidence(coverage, result);
|
|
30545
|
-
await writeFile17(
|
|
30725
|
+
await writeFile17(join44(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
|
|
30546
30726
|
`, { encoding: "utf8", mode: 384 });
|
|
30547
30727
|
if (!evidence.passed) result = { ...result, passed: false, failures: Math.max(1, result.failures) };
|
|
30548
30728
|
}
|
|
30549
30729
|
const dir = integrationDirectory(workspace, task);
|
|
30550
30730
|
await mkdir27(dir, { recursive: true });
|
|
30551
|
-
await writeFile17(
|
|
30731
|
+
await writeFile17(join44(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
|
|
30552
30732
|
`);
|
|
30553
30733
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyInteractionRun(current, result));
|
|
30554
30734
|
return { workflow: next, result, review };
|
|
@@ -30561,7 +30741,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30561
30741
|
if (mock === void 0 || !mock.exited() || result.failures === 0) return result;
|
|
30562
30742
|
const output = mock.output().trim();
|
|
30563
30743
|
try {
|
|
30564
|
-
const dir =
|
|
30744
|
+
const dir = join44(d2cOutputDirectory(workspace, task), "mock-server.log");
|
|
30565
30745
|
await writeFile17(dir, `${output}
|
|
30566
30746
|
`);
|
|
30567
30747
|
} catch {
|
|
@@ -30656,7 +30836,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30656
30836
|
if (current === void 0) throw new Error("D2C product plan is unavailable");
|
|
30657
30837
|
if (current.plan.approvedPrd !== void 0) throw new Error("The approved PRD is immutable and cannot be edited");
|
|
30658
30838
|
if (current.plan.phase !== "prd-review") throw new Error("PRD sections can only be edited during PRD review");
|
|
30659
|
-
const path =
|
|
30839
|
+
const path = join44(taskDir(workspace, task), "product", "prd.md");
|
|
30660
30840
|
const updated = await updatePrdSectionFile(path, { sectionId, body, expectedHash });
|
|
30661
30841
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30662
30842
|
await writeD2cProductPlan(workspace, {
|
|
@@ -30676,15 +30856,15 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30676
30856
|
async #assertProductPrdLocked(workspace, task) {
|
|
30677
30857
|
const current = await readD2cProductPlanView(workspace, task);
|
|
30678
30858
|
if (current?.plan.approvedPrd === void 0) return;
|
|
30679
|
-
await assertApprovedPrd(
|
|
30859
|
+
await assertApprovedPrd(join44(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
|
|
30680
30860
|
}
|
|
30681
30861
|
async #acceptancePaths(workspace, task) {
|
|
30682
|
-
const product =
|
|
30683
|
-
const openapi =
|
|
30862
|
+
const product = join44(taskDir(workspace, task), "product");
|
|
30863
|
+
const openapi = join44(product, "openapi.json");
|
|
30684
30864
|
return {
|
|
30685
|
-
prd:
|
|
30686
|
-
prototype:
|
|
30687
|
-
interaction:
|
|
30865
|
+
prd: join44(product, "prd.md"),
|
|
30866
|
+
prototype: join44(product, "prototype", "index.html"),
|
|
30867
|
+
interaction: join44(product, "prototype", "interaction-manifest.json"),
|
|
30688
30868
|
...await stat15(openapi).catch(() => void 0) === void 0 ? {} : { openapi }
|
|
30689
30869
|
};
|
|
30690
30870
|
}
|
|
@@ -30692,17 +30872,17 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30692
30872
|
const product = await readD2cProductPlanView(workspace, task);
|
|
30693
30873
|
if (product?.plan.approvedPrd === void 0) return void 0;
|
|
30694
30874
|
await this.#assertProductPrdLocked(workspace, task);
|
|
30695
|
-
const baseline = JSON.parse(await readFile35(
|
|
30875
|
+
const baseline = JSON.parse(await readFile35(join44(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
|
|
30696
30876
|
await verifyAcceptanceBaseline(await this.#acceptancePaths(workspace, task), baseline);
|
|
30697
|
-
const design =
|
|
30877
|
+
const design = join44(taskDir(workspace, task), "design");
|
|
30698
30878
|
await Promise.all([
|
|
30699
|
-
verifyAcceptanceArtifact(
|
|
30700
|
-
verifyAcceptanceArtifact(
|
|
30879
|
+
verifyAcceptanceArtifact(join44(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
|
|
30880
|
+
verifyAcceptanceArtifact(join44(design, "interaction-manifest.json"), baseline.artifacts.interaction, "runtime interaction contract")
|
|
30701
30881
|
]);
|
|
30702
30882
|
const workflow = await readWorkflow(workspace, task);
|
|
30703
30883
|
if (workflow?.openapi !== void 0) {
|
|
30704
30884
|
await verifyAcceptanceArtifact(
|
|
30705
|
-
|
|
30885
|
+
join44(taskDir(workspace, task), "integration", "swagger.json"),
|
|
30706
30886
|
{ hash: workflow.openapi.hash },
|
|
30707
30887
|
"runtime OpenAPI contract"
|
|
30708
30888
|
);
|
|
@@ -30723,14 +30903,14 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30723
30903
|
throw new Error("D2C PRD is empty and cannot be accepted");
|
|
30724
30904
|
}
|
|
30725
30905
|
if (stage === "design" && accepted) {
|
|
30726
|
-
const manifest =
|
|
30906
|
+
const manifest = join44(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
|
|
30727
30907
|
const normalizedManifest = parseInteractionManifest(await readFile35(manifest, "utf8"));
|
|
30728
30908
|
if (current.plan.approvedPrd === void 0) throw new Error("Approve the PRD before confirming the design");
|
|
30729
30909
|
verifyRequirementCoverage(current.plan.approvedPrd.criteria, normalizedManifest);
|
|
30730
30910
|
await writeFile17(manifest, `${JSON.stringify(normalizedManifest, null, 2)}
|
|
30731
30911
|
`, { encoding: "utf8", mode: 384 });
|
|
30732
30912
|
const baseline = await captureAcceptanceBaseline(await this.#acceptancePaths(workspace, task), current.plan.approvedPrd);
|
|
30733
|
-
await writeFile17(
|
|
30913
|
+
await writeFile17(join44(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
|
|
30734
30914
|
`, { encoding: "utf8", mode: 384 });
|
|
30735
30915
|
}
|
|
30736
30916
|
const plan = await writeD2cProductPlan(
|
|
@@ -30764,8 +30944,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30764
30944
|
const imported = await this.importD2cDesign(task, d2cProductPrototypeDirectory(workspace, task));
|
|
30765
30945
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30766
30946
|
if (delivery !== void 0) {
|
|
30767
|
-
const prototype = await readFile35(
|
|
30768
|
-
const interaction = await readFile35(
|
|
30947
|
+
const prototype = await readFile35(join44(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
|
|
30948
|
+
const interaction = await readFile35(join44(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json"), "utf8");
|
|
30769
30949
|
const completed = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "design", [
|
|
30770
30950
|
artifactRef("product/prototype/index.html", prototype),
|
|
30771
30951
|
artifactRef("product/prototype/interaction-manifest.json", interaction)
|
|
@@ -30849,10 +31029,10 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30849
31029
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyQualityJudgment(current, judgment));
|
|
30850
31030
|
const qualityContent = `${JSON.stringify(judgment, null, 2)}
|
|
30851
31031
|
`;
|
|
30852
|
-
await writeFile17(
|
|
31032
|
+
await writeFile17(join44(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
|
|
30853
31033
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30854
31034
|
if (next.stage === "completed" && delivery?.nodes.acceptance.status === "running") {
|
|
30855
|
-
const interactionContent = await readFile35(
|
|
31035
|
+
const interactionContent = await readFile35(join44(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
|
|
30856
31036
|
const accepted = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "acceptance", [
|
|
30857
31037
|
artifactRef("integration/interaction-results.json", interactionContent),
|
|
30858
31038
|
artifactRef("quality-judge.json", qualityContent)
|
|
@@ -31033,15 +31213,15 @@ function desktopWindowChrome(platform = process.platform) {
|
|
|
31033
31213
|
}
|
|
31034
31214
|
|
|
31035
31215
|
// src/utils/crash-guard.ts
|
|
31036
|
-
import { mkdirSync as
|
|
31037
|
-
import { join as
|
|
31216
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
31217
|
+
import { join as join45 } from "path";
|
|
31038
31218
|
var installed = false;
|
|
31039
31219
|
function installCrashGuard(options = {}) {
|
|
31040
31220
|
if (installed) return;
|
|
31041
31221
|
installed = true;
|
|
31042
31222
|
const crashLogDir = () => {
|
|
31043
31223
|
try {
|
|
31044
|
-
return
|
|
31224
|
+
return join45((options.workspace ?? process.cwd)(), ".flavor");
|
|
31045
31225
|
} catch {
|
|
31046
31226
|
return process.cwd();
|
|
31047
31227
|
}
|
|
@@ -31049,9 +31229,9 @@ function installCrashGuard(options = {}) {
|
|
|
31049
31229
|
const writeCrashLog = (kind, detail) => {
|
|
31050
31230
|
try {
|
|
31051
31231
|
const dir = crashLogDir();
|
|
31052
|
-
|
|
31053
|
-
const path =
|
|
31054
|
-
|
|
31232
|
+
mkdirSync4(dir, { recursive: true });
|
|
31233
|
+
const path = join45(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
|
|
31234
|
+
writeFileSync2(path, [
|
|
31055
31235
|
`kind: ${kind}`,
|
|
31056
31236
|
`version: ${process.version}`,
|
|
31057
31237
|
`platform: ${process.platform}`,
|
|
@@ -31243,7 +31423,7 @@ function safeGitTarget(value) {
|
|
|
31243
31423
|
// src/desktop/worktree-manager.ts
|
|
31244
31424
|
import { randomUUID as randomUUID28 } from "crypto";
|
|
31245
31425
|
import { mkdir as mkdir28, readFile as readFile37, readdir as readdir13, rm as rm17, stat as stat16, writeFile as writeFile18 } from "fs/promises";
|
|
31246
|
-
import { basename as basename10, isAbsolute as isAbsolute19, join as
|
|
31426
|
+
import { basename as basename10, isAbsolute as isAbsolute19, join as join46, relative as relative27, resolve as resolve38, sep as sep27 } from "path";
|
|
31247
31427
|
var DesktopWorktreeManager = class {
|
|
31248
31428
|
#repository;
|
|
31249
31429
|
#storage;
|
|
@@ -31289,7 +31469,7 @@ var DesktopWorktreeManager = class {
|
|
|
31289
31469
|
}
|
|
31290
31470
|
const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
|
|
31291
31471
|
try {
|
|
31292
|
-
const stored = validateStored(JSON.parse(await readFile37(
|
|
31472
|
+
const stored = validateStored(JSON.parse(await readFile37(join46(this.#storage, name), "utf8")), this.#repository, this.#storage);
|
|
31293
31473
|
if ((await stat16(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
31294
31474
|
const status = await git2(stored.path, ["status", "--porcelain"]);
|
|
31295
31475
|
const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
|
|
@@ -31353,7 +31533,7 @@ function validateStored(value, repository, storage) {
|
|
|
31353
31533
|
// src/desktop/workbench-service.ts
|
|
31354
31534
|
import { readFile as readFile38, readdir as readdir14, stat as stat17 } from "fs/promises";
|
|
31355
31535
|
import { homedir as homedir5 } from "os";
|
|
31356
|
-
import { join as
|
|
31536
|
+
import { join as join47, resolve as resolve39 } from "path";
|
|
31357
31537
|
var MAX_TEXT = 128e3;
|
|
31358
31538
|
var MAX_ITEMS = 200;
|
|
31359
31539
|
var DesktopWorkbenchService = class {
|
|
@@ -31386,11 +31566,11 @@ var DesktopWorkbenchService = class {
|
|
|
31386
31566
|
return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
|
|
31387
31567
|
}
|
|
31388
31568
|
async #goals() {
|
|
31389
|
-
const root =
|
|
31569
|
+
const root = join47(this.#workspace, ".flavor", "goals");
|
|
31390
31570
|
const names = await readdir14(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
|
|
31391
31571
|
const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
|
|
31392
31572
|
try {
|
|
31393
|
-
return GoalStateSchema.parse(JSON.parse(await boundedRead2(
|
|
31573
|
+
return GoalStateSchema.parse(JSON.parse(await boundedRead2(join47(root, name))));
|
|
31394
31574
|
} catch {
|
|
31395
31575
|
return void 0;
|
|
31396
31576
|
}
|
|
@@ -31400,7 +31580,7 @@ var DesktopWorkbenchService = class {
|
|
|
31400
31580
|
async #instructions() {
|
|
31401
31581
|
const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
31402
31582
|
const items = await Promise.all(names.map(async (name) => {
|
|
31403
|
-
const path =
|
|
31583
|
+
const path = join47(this.#workspace, name);
|
|
31404
31584
|
try {
|
|
31405
31585
|
return { name, path, content: redactErrorText(await boundedRead2(path)) };
|
|
31406
31586
|
} catch (error) {
|
|
@@ -31413,9 +31593,9 @@ var DesktopWorkbenchService = class {
|
|
|
31413
31593
|
async #permissionFiles() {
|
|
31414
31594
|
const candidates = [
|
|
31415
31595
|
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve39(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
|
|
31416
|
-
{ tier: "user", path:
|
|
31417
|
-
{ tier: "project", path:
|
|
31418
|
-
{ tier: "local", path:
|
|
31596
|
+
{ tier: "user", path: join47(this.#home, ".flavor-code", "permissions.json"), content: "" },
|
|
31597
|
+
{ tier: "project", path: join47(this.#workspace, ".flavor", "permissions.json"), content: "" },
|
|
31598
|
+
{ tier: "local", path: join47(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
|
|
31419
31599
|
];
|
|
31420
31600
|
const values = await Promise.all(candidates.map(async (item) => {
|
|
31421
31601
|
try {
|
|
@@ -31430,7 +31610,7 @@ var DesktopWorkbenchService = class {
|
|
|
31430
31610
|
async #audit() {
|
|
31431
31611
|
let raw;
|
|
31432
31612
|
try {
|
|
31433
|
-
raw = await boundedRead2(
|
|
31613
|
+
raw = await boundedRead2(join47(this.#workspace, ".flavor", "audit.jsonl"));
|
|
31434
31614
|
} catch (error) {
|
|
31435
31615
|
if (error.code === "ENOENT") return [];
|
|
31436
31616
|
throw error;
|
|
@@ -31445,7 +31625,7 @@ var DesktopWorkbenchService = class {
|
|
|
31445
31625
|
}).reverse();
|
|
31446
31626
|
}
|
|
31447
31627
|
async #context(sessionId) {
|
|
31448
|
-
const usage = await readJsonLines2(
|
|
31628
|
+
const usage = await readJsonLines2(join47(this.#workspace, ".flavor", "usage.jsonl"));
|
|
31449
31629
|
if (sessionId === void 0) return { visibility: [], usage };
|
|
31450
31630
|
try {
|
|
31451
31631
|
const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
|
|
@@ -31485,14 +31665,14 @@ function redact2(value, key = "") {
|
|
|
31485
31665
|
|
|
31486
31666
|
// src/desktop/main.ts
|
|
31487
31667
|
installCrashGuard();
|
|
31488
|
-
var moduleDirectory =
|
|
31668
|
+
var moduleDirectory = dirname25(fileURLToPath4(import.meta.url));
|
|
31489
31669
|
var developmentUrl = process.env.FLAVOR_DESKTOP_DEV_URL;
|
|
31490
31670
|
var mainWindow;
|
|
31491
31671
|
var appMenu;
|
|
31492
31672
|
var quitting = false;
|
|
31493
31673
|
function logStartup(step, detail) {
|
|
31494
31674
|
try {
|
|
31495
|
-
const logPath =
|
|
31675
|
+
const logPath = join48(dirname25(process.execPath), "flavor-code-startup.log");
|
|
31496
31676
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
31497
31677
|
appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
|
|
31498
31678
|
`);
|
|
@@ -31517,7 +31697,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
|
|
|
31517
31697
|
};
|
|
31518
31698
|
});
|
|
31519
31699
|
var d2cJudgeClient = createD2cJudgeClient();
|
|
31520
|
-
var judgeStore = () => createD2cJudgeConfigStore(
|
|
31700
|
+
var judgeStore = () => createD2cJudgeConfigStore(join48(app.getPath("userData"), "d2c-judge.json"));
|
|
31521
31701
|
function judgeImage(png) {
|
|
31522
31702
|
const image = nativeImage.createFromBuffer(png);
|
|
31523
31703
|
const size = image.getSize();
|
|
@@ -31543,7 +31723,7 @@ function sessionMetaKey(workspace, sessionId) {
|
|
|
31543
31723
|
}
|
|
31544
31724
|
function worktreeManager(workspace) {
|
|
31545
31725
|
const key = createHash28("sha256").update(resolve40(workspace)).digest("hex").slice(0, 20);
|
|
31546
|
-
return new DesktopWorktreeManager({ repository: workspace, storage:
|
|
31726
|
+
return new DesktopWorktreeManager({ repository: workspace, storage: join48(app.getPath("userData"), "task-worktrees", key) });
|
|
31547
31727
|
}
|
|
31548
31728
|
function activeWorkingDirectory() {
|
|
31549
31729
|
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
@@ -31688,7 +31868,7 @@ function createDesktopController(workspace, owner2) {
|
|
|
31688
31868
|
alias: `desktop-${basename11(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
|
|
31689
31869
|
optional: true,
|
|
31690
31870
|
startBroker: (address) => {
|
|
31691
|
-
const entry2 = app.isPackaged ?
|
|
31871
|
+
const entry2 = app.isPackaged ? join48(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join48(moduleDirectory, "pals-broker-entry.js");
|
|
31692
31872
|
return spawnPalBrokerProcess({
|
|
31693
31873
|
address,
|
|
31694
31874
|
executable: process.execPath,
|
|
@@ -31710,7 +31890,7 @@ function createDesktopController(workspace, owner2) {
|
|
|
31710
31890
|
var detachedController = createDesktopController();
|
|
31711
31891
|
var controller = detachedController;
|
|
31712
31892
|
function statePath() {
|
|
31713
|
-
return
|
|
31893
|
+
return join48(app.getPath("userData"), "desktop-state.json");
|
|
31714
31894
|
}
|
|
31715
31895
|
async function loadPersistedProjects() {
|
|
31716
31896
|
try {
|
|
@@ -31883,7 +32063,7 @@ async function chooseAndOpenWorkspace() {
|
|
|
31883
32063
|
var cachedAppIconDataUrl;
|
|
31884
32064
|
function appIconDataUrl() {
|
|
31885
32065
|
if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
|
|
31886
|
-
const icon = nativeImage.createFromPath(
|
|
32066
|
+
const icon = nativeImage.createFromPath(join48(app.getAppPath(), "assets", "icon.png"));
|
|
31887
32067
|
if (icon.isEmpty()) return void 0;
|
|
31888
32068
|
cachedAppIconDataUrl = icon.toDataURL();
|
|
31889
32069
|
return cachedAppIconDataUrl;
|
|
@@ -32301,7 +32481,7 @@ function applicationMenu() {
|
|
|
32301
32481
|
]);
|
|
32302
32482
|
}
|
|
32303
32483
|
async function createWindow() {
|
|
32304
|
-
const rendererPath =
|
|
32484
|
+
const rendererPath = join48(app.getAppPath(), "dist", "desktop-renderer", "index.html");
|
|
32305
32485
|
mainWindow = new BrowserWindow2({
|
|
32306
32486
|
title: "Flavor Code",
|
|
32307
32487
|
width: 1280,
|
|
@@ -32312,14 +32492,14 @@ async function createWindow() {
|
|
|
32312
32492
|
show: false,
|
|
32313
32493
|
...desktopWindowChrome(),
|
|
32314
32494
|
webPreferences: {
|
|
32315
|
-
preload:
|
|
32495
|
+
preload: join48(moduleDirectory, "preload.cjs"),
|
|
32316
32496
|
contextIsolation: true,
|
|
32317
32497
|
nodeIntegration: false,
|
|
32318
32498
|
sandbox: false,
|
|
32319
32499
|
webSecurity: true
|
|
32320
32500
|
}
|
|
32321
32501
|
});
|
|
32322
|
-
const iconPath =
|
|
32502
|
+
const iconPath = join48(app.getAppPath(), "assets", "icon.png");
|
|
32323
32503
|
const icon = nativeImage.createFromPath(iconPath);
|
|
32324
32504
|
if (!icon.isEmpty()) {
|
|
32325
32505
|
mainWindow.setIcon(icon);
|