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
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
MEMORY_RESTART_EXIT_CODE,
|
|
5
|
+
memoryRestartMarkerPath,
|
|
6
|
+
nextMemoryRestartMarker,
|
|
7
|
+
parseMemoryRestartMarker
|
|
8
|
+
} from "./chunk-CIVPKHF4.js";
|
|
3
9
|
import {
|
|
4
10
|
PERMISSION_MODES,
|
|
5
11
|
PermissionModeSchema,
|
|
@@ -12,7 +18,7 @@ import {
|
|
|
12
18
|
redactConfig,
|
|
13
19
|
setProjectMcpServerDisabled,
|
|
14
20
|
updateProtectedFile
|
|
15
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-HJOCNKPN.js";
|
|
16
22
|
import {
|
|
17
23
|
AuditLogger,
|
|
18
24
|
appendUsageLog,
|
|
@@ -1308,6 +1314,8 @@ function appendThinkingText(previous, delta) {
|
|
|
1308
1314
|
}
|
|
1309
1315
|
|
|
1310
1316
|
// src/ui/transcript.ts
|
|
1317
|
+
var MAX_TRANSCRIPT_TURNS = 200;
|
|
1318
|
+
var MAX_TRANSCRIPT_CHARS = 2e6;
|
|
1311
1319
|
function createTranscriptState() {
|
|
1312
1320
|
return { completed: [], nextId: 1 };
|
|
1313
1321
|
}
|
|
@@ -1604,7 +1612,7 @@ function hydrateHistory(messages, compact) {
|
|
|
1604
1612
|
}
|
|
1605
1613
|
}
|
|
1606
1614
|
if (turn !== void 0) completed.push(turn);
|
|
1607
|
-
return { completed, nextId: completed.length + 1 };
|
|
1615
|
+
return { completed: boundedCompleted(completed), nextId: completed.length + 1 };
|
|
1608
1616
|
}
|
|
1609
1617
|
function transcriptFromLegacyConversation(messages, compact) {
|
|
1610
1618
|
return hydrateHistory(messages, compact);
|
|
@@ -1698,10 +1706,10 @@ function finishActive(state) {
|
|
|
1698
1706
|
if (state.active === void 0) return state;
|
|
1699
1707
|
const active = withoutModelActivity(state.active);
|
|
1700
1708
|
return {
|
|
1701
|
-
completed: [...state.completed, {
|
|
1709
|
+
completed: boundedCompleted([...state.completed, {
|
|
1702
1710
|
...active,
|
|
1703
1711
|
completedAt: active.completedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1704
|
-
}],
|
|
1712
|
+
}]),
|
|
1705
1713
|
nextId: state.nextId,
|
|
1706
1714
|
...state.taskSnapshot === void 0 ? {} : { taskSnapshot: state.taskSnapshot }
|
|
1707
1715
|
};
|
|
@@ -1730,12 +1738,26 @@ function withoutModelActivity(turn, id) {
|
|
|
1730
1738
|
function restoreTranscriptState(state) {
|
|
1731
1739
|
const completed = state.completed.map(cloneTurn);
|
|
1732
1740
|
if (state.active !== void 0) completed.push(normalizeInterruptedTurn(cloneTurn(state.active)));
|
|
1741
|
+
const nextId = Math.max(state.nextId, ...completed.map((turn) => turn.id + 1), 1);
|
|
1733
1742
|
return {
|
|
1734
|
-
completed,
|
|
1735
|
-
nextId
|
|
1743
|
+
completed: boundedCompleted(completed),
|
|
1744
|
+
nextId,
|
|
1736
1745
|
...state.taskSnapshot === void 0 ? {} : { taskSnapshot: state.taskSnapshot }
|
|
1737
1746
|
};
|
|
1738
1747
|
}
|
|
1748
|
+
function boundedCompleted(input) {
|
|
1749
|
+
const recent = input.slice(-MAX_TRANSCRIPT_TURNS);
|
|
1750
|
+
let chars = 0;
|
|
1751
|
+
let start = recent.length;
|
|
1752
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
1753
|
+
const turn = recent[index];
|
|
1754
|
+
const size = JSON.stringify(turn).length;
|
|
1755
|
+
if (start < recent.length && chars + size > MAX_TRANSCRIPT_CHARS) break;
|
|
1756
|
+
chars += size;
|
|
1757
|
+
start = index;
|
|
1758
|
+
}
|
|
1759
|
+
return recent.slice(start);
|
|
1760
|
+
}
|
|
1739
1761
|
function compactionTurn(compact) {
|
|
1740
1762
|
const text = `Original execution steps before ${compact.compactedAt} are unavailable.`;
|
|
1741
1763
|
return {
|
|
@@ -2026,6 +2048,8 @@ function isCode(error, code) {
|
|
|
2026
2048
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2027
2049
|
import { mkdir as mkdir3, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
|
|
2028
2050
|
import { dirname as dirname5, join as join4, resolve as resolve3 } from "path";
|
|
2051
|
+
var MAX_SESSION_HISTORY_NODES = 100;
|
|
2052
|
+
var MAX_SESSION_HISTORY_CONTEXT_CHARS = 2e6;
|
|
2029
2053
|
var SessionHistory = class _SessionHistory {
|
|
2030
2054
|
#path;
|
|
2031
2055
|
#checkpoints;
|
|
@@ -2037,8 +2061,8 @@ var SessionHistory = class _SessionHistory {
|
|
|
2037
2061
|
this.#path = join4(resolve3(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
2038
2062
|
this.#checkpoints = new WorkspaceCheckpointStore({ workspace: options.workspace });
|
|
2039
2063
|
this.#restoreContext = options.restoreContext;
|
|
2040
|
-
this.#nodes = document.nodes;
|
|
2041
|
-
this.#leafId = document.leafId;
|
|
2064
|
+
this.#nodes = boundedNodes(document.nodes);
|
|
2065
|
+
this.#leafId = document.leafId !== null && this.#nodes.some((node) => node.id === document.leafId) ? document.leafId : this.#nodes.at(-1)?.id ?? null;
|
|
2042
2066
|
}
|
|
2043
2067
|
static async open(options) {
|
|
2044
2068
|
const path = join4(resolve3(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
@@ -2050,7 +2074,9 @@ var SessionHistory = class _SessionHistory {
|
|
|
2050
2074
|
} catch (error) {
|
|
2051
2075
|
if (!isCode2(error, "ENOENT")) throw error;
|
|
2052
2076
|
}
|
|
2053
|
-
|
|
2077
|
+
const history = new _SessionHistory(options, document);
|
|
2078
|
+
if (history.#nodes.length !== document.nodes.length) await history.#persist();
|
|
2079
|
+
return history;
|
|
2054
2080
|
}
|
|
2055
2081
|
get leafId() {
|
|
2056
2082
|
return this.#leafId;
|
|
@@ -2070,6 +2096,7 @@ var SessionHistory = class _SessionHistory {
|
|
|
2070
2096
|
};
|
|
2071
2097
|
this.#nodes.push(node);
|
|
2072
2098
|
this.#leafId = node.id;
|
|
2099
|
+
this.#boundNodes();
|
|
2073
2100
|
await this.#persist();
|
|
2074
2101
|
return structuredClone(node);
|
|
2075
2102
|
}
|
|
@@ -2111,6 +2138,13 @@ var SessionHistory = class _SessionHistory {
|
|
|
2111
2138
|
if (node === void 0) throw new Error(`Unknown session tree node: ${id}`);
|
|
2112
2139
|
return node;
|
|
2113
2140
|
}
|
|
2141
|
+
#boundNodes() {
|
|
2142
|
+
const bounded2 = boundedNodes(this.#nodes);
|
|
2143
|
+
this.#nodes.splice(0, this.#nodes.length, ...bounded2);
|
|
2144
|
+
if (this.#leafId !== null && !this.#nodes.some((node) => node.id === this.#leafId)) {
|
|
2145
|
+
this.#leafId = this.#nodes.at(-1)?.id ?? null;
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2114
2148
|
async #persist() {
|
|
2115
2149
|
const document = {
|
|
2116
2150
|
version: 1,
|
|
@@ -2125,6 +2159,24 @@ var SessionHistory = class _SessionHistory {
|
|
|
2125
2159
|
await rename2(temporary, this.#path);
|
|
2126
2160
|
}
|
|
2127
2161
|
};
|
|
2162
|
+
function boundedNodes(input) {
|
|
2163
|
+
const recent = input.slice(-MAX_SESSION_HISTORY_NODES);
|
|
2164
|
+
let contextChars = 0;
|
|
2165
|
+
let start = recent.length;
|
|
2166
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
2167
|
+
const node = recent[index];
|
|
2168
|
+
const size = JSON.stringify(node.context).length;
|
|
2169
|
+
if (start < recent.length && contextChars + size > MAX_SESSION_HISTORY_CONTEXT_CHARS) break;
|
|
2170
|
+
contextChars += size;
|
|
2171
|
+
start = index;
|
|
2172
|
+
}
|
|
2173
|
+
const retained = recent.slice(start).map((node) => structuredClone(node));
|
|
2174
|
+
const ids = new Set(retained.map((node) => node.id));
|
|
2175
|
+
for (const node of retained) {
|
|
2176
|
+
if (node.parentId !== null && !ids.has(node.parentId)) node.parentId = null;
|
|
2177
|
+
}
|
|
2178
|
+
return retained;
|
|
2179
|
+
}
|
|
2128
2180
|
function validate(value, sessionId) {
|
|
2129
2181
|
if (value.version !== 1 || value.sessionId !== sessionId || !Array.isArray(value.nodes)) {
|
|
2130
2182
|
throw new Error("Invalid session tree");
|
|
@@ -5326,9 +5378,10 @@ async function ensureOnce(options) {
|
|
|
5326
5378
|
|
|
5327
5379
|
// src/production.ts
|
|
5328
5380
|
import { readFile as readFile22 } from "fs/promises";
|
|
5381
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
5329
5382
|
import { createHash as createHash18, randomUUID as randomUUID20 } from "crypto";
|
|
5330
5383
|
import { homedir as homedir2, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
|
|
5331
|
-
import { basename as basename6, join as join26, relative as relative20, resolve as resolve28, sep as sep20 } from "path";
|
|
5384
|
+
import { basename as basename6, dirname as dirname19, join as join26, relative as relative20, resolve as resolve28, sep as sep20 } from "path";
|
|
5332
5385
|
|
|
5333
5386
|
// src/agent/subagents.ts
|
|
5334
5387
|
import { z as z5 } from "zod";
|
|
@@ -5877,7 +5930,9 @@ import { createHash as createHash4, randomUUID as randomUUID6 } from "crypto";
|
|
|
5877
5930
|
var DEFAULT_COMPACTION_POLICY = {
|
|
5878
5931
|
windowTokens: 2e5,
|
|
5879
5932
|
reservedOutputTokens: 2e4,
|
|
5880
|
-
|
|
5933
|
+
// 85% of the effective window (180_000 - 27_000 = 153_000): compaction used
|
|
5934
|
+
// to trigger at ~92.8%, which let sessions ride the threshold into heap OOM.
|
|
5935
|
+
autoCompactBufferTokens: 27e3,
|
|
5881
5936
|
warningBufferTokens: 2e4,
|
|
5882
5937
|
blockingBufferTokens: 3e3,
|
|
5883
5938
|
microcompactKeepRecentToolResults: 5,
|
|
@@ -6087,6 +6142,13 @@ var ContextManager = class _ContextManager {
|
|
|
6087
6142
|
#epoch;
|
|
6088
6143
|
#visibilityLog = [];
|
|
6089
6144
|
#activeTransientSystem = /* @__PURE__ */ new Map();
|
|
6145
|
+
/**
|
|
6146
|
+
* The visibility audit log persists every transient system admission and is
|
|
6147
|
+
* serialized in full on every session save. Bound both dimensions or a long
|
|
6148
|
+
* subagent-driven run grows it into hundreds of megabytes (heap OOM).
|
|
6149
|
+
*/
|
|
6150
|
+
static VISIBILITY_LOG_MAX_ENTRIES = 1e3;
|
|
6151
|
+
static VISIBILITY_RECORD_CONTENT_LIMIT = 2048;
|
|
6090
6152
|
constructor(options) {
|
|
6091
6153
|
if (options.compactAtChars !== void 0 && options.compactAtChars <= 0) throw new Error("compactAtChars must be positive");
|
|
6092
6154
|
if (options.toolOutputChars <= 0) throw new Error("toolOutputChars must be positive");
|
|
@@ -6182,11 +6244,14 @@ var ContextManager = class _ContextManager {
|
|
|
6182
6244
|
const record = {
|
|
6183
6245
|
id,
|
|
6184
6246
|
role: "system",
|
|
6185
|
-
content,
|
|
6247
|
+
content: limitVisibilityContent(content),
|
|
6186
6248
|
admittedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6187
6249
|
scope: "run"
|
|
6188
6250
|
};
|
|
6189
6251
|
this.#visibilityLog.push(record);
|
|
6252
|
+
if (this.#visibilityLog.length > _ContextManager.VISIBILITY_LOG_MAX_ENTRIES) {
|
|
6253
|
+
this.#visibilityLog = this.#visibilityLog.slice(-_ContextManager.VISIBILITY_LOG_MAX_ENTRIES);
|
|
6254
|
+
}
|
|
6190
6255
|
this.#activeTransientSystem.set(id, { role: "system", content });
|
|
6191
6256
|
return id;
|
|
6192
6257
|
}
|
|
@@ -6222,7 +6287,7 @@ var ContextManager = class _ContextManager {
|
|
|
6222
6287
|
for (const key of DYNAMIC_SOURCE_ORDER) {
|
|
6223
6288
|
const value = next[key];
|
|
6224
6289
|
if (value === void 0 || value === this.#epoch.sources[key]) continue;
|
|
6225
|
-
updates.push(contextUpdateMessage(key, displaySourceValue(key, value)));
|
|
6290
|
+
updates.push(contextUpdateMessage(key, deltaSourceValue(this.#epoch.sources[key], displaySourceValue(key, value))));
|
|
6226
6291
|
this.#epoch.sources[key] = value;
|
|
6227
6292
|
}
|
|
6228
6293
|
for (const key of DYNAMIC_SOURCE_ORDER) {
|
|
@@ -6232,8 +6297,6 @@ var ContextManager = class _ContextManager {
|
|
|
6232
6297
|
}
|
|
6233
6298
|
if (updates.length === 0) return false;
|
|
6234
6299
|
this.#messages.push(...updates);
|
|
6235
|
-
this.#lastRecordedInputTokens = void 0;
|
|
6236
|
-
this.#estimatedTokensAtLastRecordedUsage = void 0;
|
|
6237
6300
|
return true;
|
|
6238
6301
|
}
|
|
6239
6302
|
snapshot() {
|
|
@@ -6249,7 +6312,7 @@ var ContextManager = class _ContextManager {
|
|
|
6249
6312
|
const legacySummary = snapshot4.summary?.role === "system" && snapshot4.summary.content.startsWith("Conversation summary\n") ? snapshot4.summary.content.slice("Conversation summary\n".length) : void 0;
|
|
6250
6313
|
this.#compact = snapshot4.compact === void 0 ? legacySummary === void 0 ? void 0 : { summary: legacySummary, compactedAt: (/* @__PURE__ */ new Date(0)).toISOString() } : { ...snapshot4.compact };
|
|
6251
6314
|
if (snapshot4.epoch !== void 0) this.#epoch = cloneEpoch(snapshot4.epoch);
|
|
6252
|
-
this.#visibilityLog = snapshot4.visibilityLog
|
|
6315
|
+
this.#visibilityLog = boundVisibilityLog(snapshot4.visibilityLog);
|
|
6253
6316
|
this.#activeTransientSystem.clear();
|
|
6254
6317
|
this.#messages = messages.map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
|
|
6255
6318
|
this.#lastRecordedInputTokens = void 0;
|
|
@@ -6293,6 +6356,7 @@ var ContextManager = class _ContextManager {
|
|
|
6293
6356
|
async prepareForModelCall(signal = new AbortController().signal) {
|
|
6294
6357
|
signal.throwIfAborted();
|
|
6295
6358
|
this.refreshContextSources();
|
|
6359
|
+
this.#dropStaleContextUpdates();
|
|
6296
6360
|
if (!this.needsCompaction()) return false;
|
|
6297
6361
|
const originalMessages = this.#messages;
|
|
6298
6362
|
const originalRecordedUsage = this.#lastRecordedInputTokens;
|
|
@@ -6305,8 +6369,6 @@ var ContextManager = class _ContextManager {
|
|
|
6305
6369
|
const microcompact = microcompactMessages(this.#messages, this.#compaction.microcompactKeepRecentToolResults);
|
|
6306
6370
|
if (microcompact.changed) {
|
|
6307
6371
|
this.#messages = microcompact.messages;
|
|
6308
|
-
this.#lastRecordedInputTokens = void 0;
|
|
6309
|
-
this.#estimatedTokensAtLastRecordedUsage = void 0;
|
|
6310
6372
|
}
|
|
6311
6373
|
if (!this.needsCompaction()) {
|
|
6312
6374
|
if (microcompact.changed) {
|
|
@@ -6392,6 +6454,26 @@ var ContextManager = class _ContextManager {
|
|
|
6392
6454
|
this.#reportCompactProgress(100);
|
|
6393
6455
|
return true;
|
|
6394
6456
|
}
|
|
6457
|
+
#dropStaleContextUpdates() {
|
|
6458
|
+
const latestBySource = /* @__PURE__ */ new Map();
|
|
6459
|
+
for (let index = this.#messages.length - 1; index >= 0; index -= 1) {
|
|
6460
|
+
const source = contextUpdateSource(this.#messages[index]);
|
|
6461
|
+
if (source === void 0 || latestBySource.has(source)) continue;
|
|
6462
|
+
latestBySource.set(source, index);
|
|
6463
|
+
}
|
|
6464
|
+
const next = [];
|
|
6465
|
+
let changed = false;
|
|
6466
|
+
this.#messages.forEach((message2, index) => {
|
|
6467
|
+
const source = contextUpdateSource(message2);
|
|
6468
|
+
if (source !== void 0 && latestBySource.get(source) !== index) {
|
|
6469
|
+
changed = true;
|
|
6470
|
+
return;
|
|
6471
|
+
}
|
|
6472
|
+
next.push(message2);
|
|
6473
|
+
});
|
|
6474
|
+
if (changed) this.#messages = next;
|
|
6475
|
+
return changed;
|
|
6476
|
+
}
|
|
6395
6477
|
#reportCompactProgress(percentage) {
|
|
6396
6478
|
const normalized = Math.max(0, Math.min(100, Math.floor(percentage / 10) * 10));
|
|
6397
6479
|
if (normalized === this.#lastCompactProgress) return;
|
|
@@ -6503,6 +6585,33 @@ function contextUpdateMessage(source, content) {
|
|
|
6503
6585
|
return { role: "system", content: `Context update [${source}]
|
|
6504
6586
|
${content}` };
|
|
6505
6587
|
}
|
|
6588
|
+
var CONTEXT_UPDATE_PREFIX = "Context update [";
|
|
6589
|
+
function contextUpdateSource(message2) {
|
|
6590
|
+
if (message2.role !== "system" || typeof message2.content !== "string") return void 0;
|
|
6591
|
+
if (!message2.content.startsWith(CONTEXT_UPDATE_PREFIX)) return void 0;
|
|
6592
|
+
const end = message2.content.indexOf("]\n", CONTEXT_UPDATE_PREFIX.length);
|
|
6593
|
+
if (end < 0) return void 0;
|
|
6594
|
+
return message2.content.slice(CONTEXT_UPDATE_PREFIX.length, end);
|
|
6595
|
+
}
|
|
6596
|
+
function deltaSourceValue(previous, display) {
|
|
6597
|
+
if (previous === void 0 || !display.startsWith(previous)) return display;
|
|
6598
|
+
return display.slice(previous.length).replace(/^\s+/, "") || display;
|
|
6599
|
+
}
|
|
6600
|
+
function limitVisibilityContent(content) {
|
|
6601
|
+
const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
|
|
6602
|
+
if (content.length <= limit) return content;
|
|
6603
|
+
return `${content.slice(0, limit)}
|
|
6604
|
+
...[truncated; original length: ${content.length} characters]...`;
|
|
6605
|
+
}
|
|
6606
|
+
function boundVisibilityLog(records) {
|
|
6607
|
+
if (records === void 0) return [];
|
|
6608
|
+
const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
|
|
6609
|
+
const bounded2 = records.map((item) => ({
|
|
6610
|
+
...item,
|
|
6611
|
+
...item.content.length <= limit ? {} : { content: limitVisibilityContent(item.content) }
|
|
6612
|
+
}));
|
|
6613
|
+
return bounded2.length > ContextManager.VISIBILITY_LOG_MAX_ENTRIES ? bounded2.slice(-ContextManager.VISIBILITY_LOG_MAX_ENTRIES) : bounded2;
|
|
6614
|
+
}
|
|
6506
6615
|
function cloneEpoch(epoch) {
|
|
6507
6616
|
return {
|
|
6508
6617
|
...epoch,
|
|
@@ -6797,6 +6906,9 @@ function reportProgress(callback, percentage) {
|
|
|
6797
6906
|
}
|
|
6798
6907
|
}
|
|
6799
6908
|
|
|
6909
|
+
// src/agent/loop.ts
|
|
6910
|
+
import { getHeapStatistics } from "v8";
|
|
6911
|
+
|
|
6800
6912
|
// src/models/structured.ts
|
|
6801
6913
|
import { z as z7 } from "zod";
|
|
6802
6914
|
var DEFAULT_RETRY_DELAYS = [1e3, 2e3, 4e3];
|
|
@@ -7214,6 +7326,18 @@ function envMaxIterations() {
|
|
|
7214
7326
|
if (!Number.isInteger(parsed) || parsed <= 0) return void 0;
|
|
7215
7327
|
return parsed;
|
|
7216
7328
|
}
|
|
7329
|
+
var MEMORY_PRESSURE_HEAP_RATIO = 0.8;
|
|
7330
|
+
function memoryPressureError(heapUsed, heapLimit) {
|
|
7331
|
+
if (heapLimit <= 0 || heapUsed < heapLimit * MEMORY_PRESSURE_HEAP_RATIO) return void 0;
|
|
7332
|
+
const mb = (bytes) => Math.round(bytes / 1048576);
|
|
7333
|
+
return {
|
|
7334
|
+
code: "memory_pressure",
|
|
7335
|
+
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.`
|
|
7336
|
+
};
|
|
7337
|
+
}
|
|
7338
|
+
function currentMemoryPressure() {
|
|
7339
|
+
return memoryPressureError(process.memoryUsage().heapUsed, getHeapStatistics().heap_size_limit);
|
|
7340
|
+
}
|
|
7217
7341
|
var AgentLoop = class {
|
|
7218
7342
|
#options;
|
|
7219
7343
|
#iterationLimitMode = "standard";
|
|
@@ -7284,6 +7408,11 @@ var AgentLoop = class {
|
|
|
7284
7408
|
yield { type: "error", error: { code: "cancelled", message: abortMessage(request.signal) } };
|
|
7285
7409
|
return;
|
|
7286
7410
|
}
|
|
7411
|
+
const pressure = currentMemoryPressure();
|
|
7412
|
+
if (pressure !== void 0) {
|
|
7413
|
+
yield { type: "error", error: pressure };
|
|
7414
|
+
return;
|
|
7415
|
+
}
|
|
7287
7416
|
const softLimit = Math.floor(maxIterations * this.#options.softLimitFactor);
|
|
7288
7417
|
if (!warned && iteration >= softLimit) {
|
|
7289
7418
|
warned = true;
|
|
@@ -7323,6 +7452,11 @@ var AgentLoop = class {
|
|
|
7323
7452
|
yield { type: "error", error: normalizeProviderError(error) };
|
|
7324
7453
|
return;
|
|
7325
7454
|
}
|
|
7455
|
+
const preparedPressure = currentMemoryPressure();
|
|
7456
|
+
if (preparedPressure !== void 0) {
|
|
7457
|
+
yield { type: "error", error: preparedPressure };
|
|
7458
|
+
return;
|
|
7459
|
+
}
|
|
7326
7460
|
let assistantText = "";
|
|
7327
7461
|
const collectedToolCalls = [];
|
|
7328
7462
|
const collectedThinking = [];
|
|
@@ -7346,6 +7480,11 @@ var AgentLoop = class {
|
|
|
7346
7480
|
tools: [...this.#options.tools],
|
|
7347
7481
|
...request.signal === void 0 ? {} : { signal: request.signal }
|
|
7348
7482
|
};
|
|
7483
|
+
const requestPressure = currentMemoryPressure();
|
|
7484
|
+
if (requestPressure !== void 0) {
|
|
7485
|
+
yield { type: "error", error: requestPressure };
|
|
7486
|
+
return;
|
|
7487
|
+
}
|
|
7349
7488
|
let before;
|
|
7350
7489
|
try {
|
|
7351
7490
|
before = await this.#options.hooks.emit({
|
|
@@ -12679,7 +12818,8 @@ var BoundedOutput = class {
|
|
|
12679
12818
|
#headLimit;
|
|
12680
12819
|
#tailLimit;
|
|
12681
12820
|
#head = Buffer.alloc(0);
|
|
12682
|
-
#
|
|
12821
|
+
#tailChunks = [];
|
|
12822
|
+
#tailBytes = 0;
|
|
12683
12823
|
#complete = Buffer.alloc(0);
|
|
12684
12824
|
#bytes = 0;
|
|
12685
12825
|
constructor(limit) {
|
|
@@ -12699,15 +12839,24 @@ var BoundedOutput = class {
|
|
|
12699
12839
|
const count = Math.min(this.#headLimit - this.#head.length, chunk.length);
|
|
12700
12840
|
this.#head = Buffer.concat([this.#head, chunk.subarray(0, count)]);
|
|
12701
12841
|
}
|
|
12702
|
-
if (this.#tailLimit > 0)
|
|
12842
|
+
if (this.#tailLimit > 0) {
|
|
12843
|
+
this.#tailChunks.push(chunk);
|
|
12844
|
+
this.#tailBytes += chunk.length;
|
|
12845
|
+
if (this.#tailBytes >= this.#tailLimit * 2) {
|
|
12846
|
+
const merged = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
12847
|
+
this.#tailChunks = [merged];
|
|
12848
|
+
this.#tailBytes = merged.length;
|
|
12849
|
+
}
|
|
12850
|
+
}
|
|
12703
12851
|
}
|
|
12704
12852
|
metadata() {
|
|
12705
12853
|
return { truncated: this.truncated, originalBytes: this.#bytes, limitBytes: this.#limit };
|
|
12706
12854
|
}
|
|
12707
12855
|
text() {
|
|
12708
12856
|
if (!this.truncated) return decodeOutput(this.#complete);
|
|
12857
|
+
const tail = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
12709
12858
|
const encoding = classifyUtf8(this.#complete) === "invalid" && process.platform === "win32" ? "gb18030" : "utf-8";
|
|
12710
|
-
return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(
|
|
12859
|
+
return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(tail, encoding)}`;
|
|
12711
12860
|
}
|
|
12712
12861
|
};
|
|
12713
12862
|
var AdaptiveOutputDecoder = class {
|
|
@@ -22634,7 +22783,7 @@ async function createProductionRuntime(options) {
|
|
|
22634
22783
|
contextEpoch: harness.main.context.snapshot().epoch?.id ?? "legacy"
|
|
22635
22784
|
});
|
|
22636
22785
|
const turnId = harnessJournal.startTurn(turnConfig, { prompt, initialUserMessage: runOptions?.initialUserMessage });
|
|
22637
|
-
return durableTurn(
|
|
22786
|
+
return monitorMemoryRestart(durableTurn(persistAfter(runMain(
|
|
22638
22787
|
harness,
|
|
22639
22788
|
skills,
|
|
22640
22789
|
prompt,
|
|
@@ -22650,11 +22799,7 @@ async function createProductionRuntime(options) {
|
|
|
22650
22799
|
runOptions?.initialUserMessage,
|
|
22651
22800
|
runOptions?.additionalContext,
|
|
22652
22801
|
ide
|
|
22653
|
-
), persist, () =>
|
|
22654
|
-
prompt,
|
|
22655
|
-
context: harness.main.context.snapshot(),
|
|
22656
|
-
label: `turn: ${prompt.slice(0, 80)}`
|
|
22657
|
-
})), harnessJournal, turnId, turnConfig);
|
|
22802
|
+
), persist), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
|
|
22658
22803
|
},
|
|
22659
22804
|
runSkill: (skill, prompt, signal) => persistAfter(
|
|
22660
22805
|
runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
|
|
@@ -23364,14 +23509,6 @@ async function* persistAfter(source, persist) {
|
|
|
23364
23509
|
await persist();
|
|
23365
23510
|
}
|
|
23366
23511
|
}
|
|
23367
|
-
async function* persistAndCheckpointAfter(source, persist, checkpoint) {
|
|
23368
|
-
try {
|
|
23369
|
-
for await (const item of source) yield item;
|
|
23370
|
-
await checkpoint();
|
|
23371
|
-
} finally {
|
|
23372
|
-
await persist();
|
|
23373
|
-
}
|
|
23374
|
-
}
|
|
23375
23512
|
async function* persistEach(source, persist) {
|
|
23376
23513
|
try {
|
|
23377
23514
|
for await (const item of source) {
|
|
@@ -23403,6 +23540,31 @@ async function* durableTurn(source, journal, turnId, config) {
|
|
|
23403
23540
|
}
|
|
23404
23541
|
}
|
|
23405
23542
|
}
|
|
23543
|
+
async function* monitorMemoryRestart(source, onRestartNeeded) {
|
|
23544
|
+
let pressure = false;
|
|
23545
|
+
for await (const event of source) {
|
|
23546
|
+
if (event.type === "error" && event.error.code === "memory_pressure") pressure = true;
|
|
23547
|
+
yield event;
|
|
23548
|
+
}
|
|
23549
|
+
if (pressure) onRestartNeeded();
|
|
23550
|
+
}
|
|
23551
|
+
function requestMemoryRestart(workspace, sessionId, emitOutput) {
|
|
23552
|
+
try {
|
|
23553
|
+
const markerPath = memoryRestartMarkerPath(workspace);
|
|
23554
|
+
let previous;
|
|
23555
|
+
try {
|
|
23556
|
+
previous = parseMemoryRestartMarker(readFileSync3(markerPath, "utf8"));
|
|
23557
|
+
} catch {
|
|
23558
|
+
}
|
|
23559
|
+
mkdirSync3(dirname19(markerPath), { recursive: true });
|
|
23560
|
+
writeFileSync(markerPath, `${JSON.stringify(nextMemoryRestartMarker(previous, sessionId, /* @__PURE__ */ new Date()))}
|
|
23561
|
+
`, "utf8");
|
|
23562
|
+
} catch {
|
|
23563
|
+
}
|
|
23564
|
+
process.exitCode = MEMORY_RESTART_EXIT_CODE;
|
|
23565
|
+
emitOutput({ type: "notice", message: "Memory watermark reached; the session is saved and Flavor is restarting with a fresh heap." });
|
|
23566
|
+
emitOutput({ type: "exit" });
|
|
23567
|
+
}
|
|
23406
23568
|
async function runGitCommit(deps, hint, signal) {
|
|
23407
23569
|
if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
|
|
23408
23570
|
let summary2 = await changeSummary(deps.workspace);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/utils/memory-restart.ts
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
var MEMORY_RESTART_EXIT_CODE = 75;
|
|
7
|
+
var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
|
|
8
|
+
var MEMORY_RESTART_MAX_ATTEMPTS = 3;
|
|
9
|
+
var MEMORY_RESTART_WINDOW_MS = 30 * 60 * 1e3;
|
|
10
|
+
var SESSION_ID_PATTERN = /^session-[A-Za-z0-9_-]+$/u;
|
|
11
|
+
function memoryRestartMarkerPath(workspace) {
|
|
12
|
+
return join(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
|
|
13
|
+
}
|
|
14
|
+
function nextMemoryRestartMarker(previous, sessionId, now) {
|
|
15
|
+
const reusable = previous !== void 0 && previous.sessionId === sessionId && now.getTime() - Date.parse(previous.requestedAt) < MEMORY_RESTART_WINDOW_MS && Number.isInteger(previous.attempts) && previous.attempts > 0;
|
|
16
|
+
return { sessionId, requestedAt: now.toISOString(), attempts: reusable ? previous.attempts + 1 : 1 };
|
|
17
|
+
}
|
|
18
|
+
function shouldRelaunchForMemoryRestart(marker) {
|
|
19
|
+
return marker !== void 0 && SESSION_ID_PATTERN.test(marker.sessionId) && Number.isInteger(marker.attempts) && marker.attempts >= 1 && marker.attempts <= MEMORY_RESTART_MAX_ATTEMPTS;
|
|
20
|
+
}
|
|
21
|
+
function memoryRestartArgs(argv, marker) {
|
|
22
|
+
if (!shouldRelaunchForMemoryRestart(marker)) return void 0;
|
|
23
|
+
const stripped = [];
|
|
24
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
25
|
+
const argument = argv[index];
|
|
26
|
+
if (argument === "--resume") {
|
|
27
|
+
if (index + 1 < argv.length && !argv[index + 1].startsWith("-")) index += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (argument.startsWith("--resume=")) continue;
|
|
31
|
+
stripped.push(argument);
|
|
32
|
+
}
|
|
33
|
+
return [...stripped, "--resume", marker.sessionId];
|
|
34
|
+
}
|
|
35
|
+
function parseMemoryRestartMarker(json) {
|
|
36
|
+
try {
|
|
37
|
+
const value = JSON.parse(json);
|
|
38
|
+
if (typeof value.sessionId !== "string" || typeof value.requestedAt !== "string" || typeof value.attempts !== "number") {
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
return { sessionId: value.sessionId, requestedAt: value.requestedAt, attempts: value.attempts };
|
|
42
|
+
} catch {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
MEMORY_RESTART_EXIT_CODE,
|
|
49
|
+
memoryRestartMarkerPath,
|
|
50
|
+
nextMemoryRestartMarker,
|
|
51
|
+
memoryRestartArgs,
|
|
52
|
+
parseMemoryRestartMarker
|
|
53
|
+
};
|
|
@@ -129,7 +129,7 @@ var FlavorConfigSchema = z.object({
|
|
|
129
129
|
context: z.object({
|
|
130
130
|
windowTokens: z.number().int().positive().default(2e5),
|
|
131
131
|
reservedOutputTokens: z.number().int().nonnegative().default(2e4),
|
|
132
|
-
autoCompactBufferTokens: z.number().int().nonnegative().default(
|
|
132
|
+
autoCompactBufferTokens: z.number().int().nonnegative().default(27e3),
|
|
133
133
|
warningBufferTokens: z.number().int().nonnegative().default(2e4),
|
|
134
134
|
blockingBufferTokens: z.number().int().nonnegative().default(3e3),
|
|
135
135
|
microcompactKeepRecentToolResults: z.number().int().nonnegative().default(5),
|
package/dist/cli-main.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module"; const require = createRequire(impor
|
|
|
3
3
|
import {
|
|
4
4
|
FlavorRpcServer,
|
|
5
5
|
TraceRecorder
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RV3PTIQE.js";
|
|
7
7
|
import {
|
|
8
8
|
staticTaskLines
|
|
9
9
|
} from "./chunk-2BF7CVWZ.js";
|
|
@@ -23,7 +23,8 @@ import {
|
|
|
23
23
|
packageVersion,
|
|
24
24
|
redactErrorText,
|
|
25
25
|
runPalBroker
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-BKOOYSGH.js";
|
|
27
|
+
import "./chunk-CIVPKHF4.js";
|
|
27
28
|
import {
|
|
28
29
|
McpServerConfigSchema,
|
|
29
30
|
McpServerNameSchema,
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
readRecoverableFile,
|
|
33
34
|
setProjectSkillDisabled,
|
|
34
35
|
updateProtectedFile
|
|
35
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-HJOCNKPN.js";
|
|
36
37
|
import "./chunk-RQTYHUMK.js";
|
|
37
38
|
import "./chunk-S32JRDZK.js";
|
|
38
39
|
|
|
@@ -750,7 +751,7 @@ async function runEvaluationFile(path, outputPath) {
|
|
|
750
751
|
})),
|
|
751
752
|
...parsed.maxTokens === void 0 ? {} : { maxTokens: parsed.maxTokens }
|
|
752
753
|
};
|
|
753
|
-
const configured = await import("./load-
|
|
754
|
+
const configured = await import("./load-DELQW42K.js").then(({ loadConfig: loadConfig2 }) => loadConfig2({ cwd: workspace, home: process.env.USERPROFILE ?? process.env.HOME ?? workspace }));
|
|
754
755
|
const environment = createExecutionEnvironment(workspace, configured.config.execution) ?? new LocalExecutionEnvironment();
|
|
755
756
|
let report;
|
|
756
757
|
try {
|
|
@@ -875,9 +876,9 @@ function createProgram(dependencies = {}) {
|
|
|
875
876
|
}
|
|
876
877
|
async function runInteractiveCli(props) {
|
|
877
878
|
const [{ render, AlternateScreen }, { createElement }, { App }] = await Promise.all([
|
|
878
|
-
import("./claude-ink-
|
|
879
|
+
import("./claude-ink-AUZBJ3BE.js"),
|
|
879
880
|
import("react"),
|
|
880
|
-
import("./app-
|
|
881
|
+
import("./app-VPS5QIRK.js")
|
|
881
882
|
]);
|
|
882
883
|
const instance = await render(createElement(
|
|
883
884
|
AlternateScreen,
|