flavor-code 1.4.3-beta.2 → 1.4.4-beta.2
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 +27 -2
- package/README.zh-CN.md +27 -2
- package/dist/agent/loop.d.ts +1 -0
- package/dist/{app-G35YBHPR.js → app-C5I2TH7I.js} +399 -109
- package/dist/{chunk-FAJ6YDZK.js → chunk-36IDJRLQ.js} +1 -1
- package/dist/{chunk-HGTCIHWA.js → chunk-5BDKRA6U.js} +30 -1
- package/dist/{chunk-T6RE76D2.js → chunk-IKGT4MDT.js} +35 -23
- package/dist/{chunk-K7UGGWJV.js → chunk-JQM2L2YM.js} +1 -1
- package/dist/{chunk-6X62CGMA.js → chunk-ONRY3C2L.js} +3 -0
- package/dist/{chunk-IDJAHEG6.js → chunk-U4XTQ5PU.js} +392 -188
- package/dist/{chunk-SJUIIHIA.js → chunk-X6U6BO5D.js} +1 -1
- package/dist/{claude-ink-BE56DB6N.js → claude-ink-F554I3GJ.js} +1 -1
- package/dist/{cli-3MF6EL2H.js → cli-V3BLZ5UW.js} +7 -7
- package/dist/cli-main.js +16 -16
- package/dist/config/protected-file.d.ts +2 -0
- package/dist/config/schema.d.ts +3 -0
- package/dist/context/global-instructions.d.ts +12 -0
- package/dist/context/manager.d.ts +2 -0
- package/dist/desktop/main.js +660 -419
- package/dist/desktop-renderer/assets/{index-DCqIrF1S.js → index-CjC0Ibiq.js} +2 -2
- package/dist/desktop-renderer/assets/{interactive-terminal-B6GbpzIn.js → interactive-terminal-IjIT3Xoa.js} +1 -1
- package/dist/desktop-renderer/index.html +1 -1
- package/dist/{doctor-OHCGK52S.js → doctor-SEL32R7M.js} +3 -3
- package/dist/harness/local.d.ts +1 -0
- package/dist/{load-QK6CMLDF.js → load-RHWXNUCS.js} +2 -2
- package/dist/{manager-5SI4MPES.js → manager-IYBNNKUP.js} +2 -2
- package/dist/{production-6DIZ6YLQ.js → production-TD7UWWXW.js} +6 -6
- package/dist/production.d.ts +2 -0
- package/dist/sdk/index.js +6 -6
- package/dist/{store-ZIHN6363.js → store-MZZERW7K.js} +2 -2
- package/dist/ui/commands.d.ts +11 -3
- package/dist/ui/session.d.ts +2 -1
- package/package.json +1 -1
- package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +649 -1
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
rgPath,
|
|
18
18
|
runDoctor,
|
|
19
19
|
terminateProcessTree
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-JQM2L2YM.js";
|
|
21
21
|
import {
|
|
22
22
|
execFileNoThrow
|
|
23
23
|
} from "./chunk-IU3UBETC.js";
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
normalizeMemoryContent,
|
|
50
50
|
registerCrashCleanup,
|
|
51
51
|
renderMemoryDocument
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-36IDJRLQ.js";
|
|
53
53
|
import {
|
|
54
54
|
redactErrorText,
|
|
55
55
|
redactSecrets
|
|
@@ -94,8 +94,9 @@ import {
|
|
|
94
94
|
readRecoverableFile,
|
|
95
95
|
redactConfig,
|
|
96
96
|
setProjectMcpServerDisabled,
|
|
97
|
+
updatePlainTextFile,
|
|
97
98
|
updateProtectedFile
|
|
98
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-5BDKRA6U.js";
|
|
99
100
|
import {
|
|
100
101
|
SESSION_VERSION,
|
|
101
102
|
SessionStore,
|
|
@@ -120,14 +121,14 @@ import {
|
|
|
120
121
|
transcriptReducer,
|
|
121
122
|
updatePlanTask,
|
|
122
123
|
withTimeout
|
|
123
|
-
} from "./chunk-
|
|
124
|
+
} from "./chunk-X6U6BO5D.js";
|
|
124
125
|
import {
|
|
125
126
|
message
|
|
126
127
|
} from "./chunk-BDX777JR.js";
|
|
127
128
|
import {
|
|
128
129
|
PERMISSION_MODES,
|
|
129
130
|
normalizePermissionMode
|
|
130
|
-
} from "./chunk-
|
|
131
|
+
} from "./chunk-ONRY3C2L.js";
|
|
131
132
|
import {
|
|
132
133
|
NEVER,
|
|
133
134
|
ZodIssueCode,
|
|
@@ -8288,11 +8289,11 @@ var require_ignore = __commonJS({
|
|
|
8288
8289
|
});
|
|
8289
8290
|
|
|
8290
8291
|
// src/production.ts
|
|
8291
|
-
import { readFile as
|
|
8292
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
8292
8293
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
8293
8294
|
import { createHash as createHash16, randomUUID as randomUUID16 } from "crypto";
|
|
8294
8295
|
import { homedir, release as osRelease, tmpdir as tmpdir3, version as osVersion } from "os";
|
|
8295
|
-
import { basename as basename4, dirname as dirname13, join as
|
|
8296
|
+
import { basename as basename4, dirname as dirname13, join as join22, relative as relative16, resolve as resolve25, sep as sep16 } from "path";
|
|
8296
8297
|
import { performance } from "perf_hooks";
|
|
8297
8298
|
|
|
8298
8299
|
// src/agent/task-tools.ts
|
|
@@ -8527,6 +8528,7 @@ var ContextManager = class _ContextManager {
|
|
|
8527
8528
|
#workspaceInstructions;
|
|
8528
8529
|
#memory;
|
|
8529
8530
|
#userMemory;
|
|
8531
|
+
#globalInstructions;
|
|
8530
8532
|
#compactAtChars;
|
|
8531
8533
|
#toolOutputChars;
|
|
8532
8534
|
#recentTurns;
|
|
@@ -8571,6 +8573,7 @@ var ContextManager = class _ContextManager {
|
|
|
8571
8573
|
this.#workspaceInstructions = options.workspaceInstructions;
|
|
8572
8574
|
this.#memory = options.memory;
|
|
8573
8575
|
this.#userMemory = options.userMemory;
|
|
8576
|
+
this.#globalInstructions = options.globalInstructions;
|
|
8574
8577
|
this.#taskState = options.taskState;
|
|
8575
8578
|
this.#compactAtChars = options.compactAtChars ?? Number.POSITIVE_INFINITY;
|
|
8576
8579
|
this.#toolOutputChars = options.toolOutputChars;
|
|
@@ -8610,6 +8613,7 @@ var ContextManager = class _ContextManager {
|
|
|
8610
8613
|
...this.#memory === void 0 ? {} : { memory: this.#memory },
|
|
8611
8614
|
...this.#taskState === void 0 ? {} : { taskState: this.#taskState },
|
|
8612
8615
|
...userMemory === void 0 ? {} : { userMemory },
|
|
8616
|
+
...this.#globalInstructions === void 0 ? {} : { globalInstructions: this.#globalInstructions },
|
|
8613
8617
|
compactAtChars: this.#compactAtChars,
|
|
8614
8618
|
toolOutputChars: this.#toolOutputChars,
|
|
8615
8619
|
compaction: this.#compaction,
|
|
@@ -8691,12 +8695,14 @@ var ContextManager = class _ContextManager {
|
|
|
8691
8695
|
for (const key of DYNAMIC_SOURCE_ORDER) {
|
|
8692
8696
|
const value = next[key];
|
|
8693
8697
|
if (value === void 0 || value === this.#epoch.sources[key]) continue;
|
|
8694
|
-
|
|
8698
|
+
const display = key === "global-instructions" ? this.#epoch.sources[key] === void 0 ? value : `These replace all earlier global instructions.
|
|
8699
|
+
${value}` : deltaSourceValue(this.#epoch.sources[key], displaySourceValue(key, value));
|
|
8700
|
+
updates.push(contextUpdateMessage(key, display));
|
|
8695
8701
|
this.#epoch.sources[key] = value;
|
|
8696
8702
|
}
|
|
8697
8703
|
for (const key of DYNAMIC_SOURCE_ORDER) {
|
|
8698
8704
|
if (key in next || !(key in this.#epoch.sources)) continue;
|
|
8699
|
-
updates.push(contextUpdateMessage(key, "(removed)"));
|
|
8705
|
+
updates.push(contextUpdateMessage(key, key === "global-instructions" ? "Global instructions were removed; disregard all earlier global instructions." : "(removed)"));
|
|
8700
8706
|
delete this.#epoch.sources[key];
|
|
8701
8707
|
}
|
|
8702
8708
|
if (updates.length === 0) return false;
|
|
@@ -8716,9 +8722,15 @@ var ContextManager = class _ContextManager {
|
|
|
8716
8722
|
const legacySummary = snapshot3.summary?.role === "system" && snapshot3.summary.content.startsWith("Conversation summary\n") ? snapshot3.summary.content.slice("Conversation summary\n".length) : void 0;
|
|
8717
8723
|
this.#compact = snapshot3.compact === void 0 ? legacySummary === void 0 ? void 0 : { summary: legacySummary, compactedAt: (/* @__PURE__ */ new Date(0)).toISOString() } : { ...snapshot3.compact };
|
|
8718
8724
|
if (snapshot3.epoch !== void 0) this.#epoch = cloneEpoch(snapshot3.epoch);
|
|
8725
|
+
const globalInstructions = tryResolveOptionalSystemSections(this.#globalInstructions);
|
|
8726
|
+
const globalInstructionsAbsent = globalInstructions.available && (globalInstructions.sections?.length ?? 0) === 0;
|
|
8727
|
+
if (globalInstructionsAbsent) {
|
|
8728
|
+
delete this.#epoch.sources["global-instructions"];
|
|
8729
|
+
if (this.#epoch.pinnedSources !== void 0) delete this.#epoch.pinnedSources["global-instructions"];
|
|
8730
|
+
}
|
|
8719
8731
|
this.#visibilityLog = boundVisibilityLog(snapshot3.visibilityLog);
|
|
8720
8732
|
this.#activeTransientSystem.clear();
|
|
8721
|
-
this.#messages = messages.map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
|
|
8733
|
+
this.#messages = (globalInstructionsAbsent ? messages.filter((message2) => message2.role !== "system" || !modelContentText(message2.content).startsWith("Context update [global-instructions]\n")) : messages).map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
|
|
8722
8734
|
this.#lastRecordedInputTokens = void 0;
|
|
8723
8735
|
this.#estimatedTokensAtLastRecordedUsage = void 0;
|
|
8724
8736
|
this.#consecutiveAutoCompactFailures = 0;
|
|
@@ -8934,6 +8946,12 @@ ${userMemory}`,
|
|
|
8934
8946
|
}
|
|
8935
8947
|
#resolvedDynamicSources(previous = {}) {
|
|
8936
8948
|
const result = {};
|
|
8949
|
+
const globalInstructions = tryResolveOptionalSystemSections(this.#globalInstructions);
|
|
8950
|
+
if (!globalInstructions.available && previous["global-instructions"] !== void 0) {
|
|
8951
|
+
result["global-instructions"] = previous["global-instructions"];
|
|
8952
|
+
} else if (globalInstructions.sections !== void 0 && globalInstructions.sections.length > 0) {
|
|
8953
|
+
result["global-instructions"] = globalInstructions.sections.join("\n\n");
|
|
8954
|
+
}
|
|
8937
8955
|
if (this.#memory !== void 0) result["long-term-memory"] = this.#memory;
|
|
8938
8956
|
if (this.#taskState !== void 0) result["task-state"] = this.#taskState;
|
|
8939
8957
|
const volatile = tryResolveOptionalSystemSections(this.#volatileSystem);
|
|
@@ -8942,7 +8960,7 @@ ${userMemory}`,
|
|
|
8942
8960
|
return result;
|
|
8943
8961
|
}
|
|
8944
8962
|
};
|
|
8945
|
-
var DYNAMIC_SOURCE_ORDER = ["long-term-memory", "task-state", "runtime"];
|
|
8963
|
+
var DYNAMIC_SOURCE_ORDER = ["global-instructions", "long-term-memory", "task-state", "runtime"];
|
|
8946
8964
|
function sourceMessages(sources) {
|
|
8947
8965
|
return DYNAMIC_SOURCE_ORDER.flatMap((key) => {
|
|
8948
8966
|
const content = sources[key];
|
|
@@ -8956,7 +8974,7 @@ function sourceMessages(sources) {
|
|
|
8956
8974
|
} catch {
|
|
8957
8975
|
}
|
|
8958
8976
|
}
|
|
8959
|
-
const label = key === "long-term-memory" ? "Long-term memory" : key === "task-state" ? "Task state" : "Runtime";
|
|
8977
|
+
const label = key === "global-instructions" ? "Global instructions" : key === "long-term-memory" ? "Long-term memory" : key === "task-state" ? "Task state" : "Runtime";
|
|
8960
8978
|
return [{
|
|
8961
8979
|
role: "system",
|
|
8962
8980
|
content: `${label}
|
|
@@ -9112,9 +9130,122 @@ function providerValidMessages(input) {
|
|
|
9112
9130
|
return output;
|
|
9113
9131
|
}
|
|
9114
9132
|
|
|
9133
|
+
// src/context/global-instructions.ts
|
|
9134
|
+
import { lstat, readFile } from "fs/promises";
|
|
9135
|
+
import { join } from "path";
|
|
9136
|
+
var GlobalInstructions = class _GlobalInstructions {
|
|
9137
|
+
static MAX_BYTES = 64 * 1024;
|
|
9138
|
+
static MAX_RULE_BYTES = 4 * 1024;
|
|
9139
|
+
path;
|
|
9140
|
+
#content;
|
|
9141
|
+
constructor(home) {
|
|
9142
|
+
this.path = join(home, ".flavor-code", "GLOBAL.md");
|
|
9143
|
+
}
|
|
9144
|
+
get content() {
|
|
9145
|
+
return this.#content;
|
|
9146
|
+
}
|
|
9147
|
+
async refresh() {
|
|
9148
|
+
let info;
|
|
9149
|
+
try {
|
|
9150
|
+
info = await lstat(this.path);
|
|
9151
|
+
} catch (error) {
|
|
9152
|
+
if (isMissing(error)) {
|
|
9153
|
+
this.#content = void 0;
|
|
9154
|
+
return;
|
|
9155
|
+
}
|
|
9156
|
+
throw error;
|
|
9157
|
+
}
|
|
9158
|
+
if (!info.isFile()) throw new Error(`Global instructions must be a regular file: ${this.path}`);
|
|
9159
|
+
if (info.size > _GlobalInstructions.MAX_BYTES) {
|
|
9160
|
+
throw new Error(`Global instructions exceed ${_GlobalInstructions.MAX_BYTES} bytes: ${this.path}`);
|
|
9161
|
+
}
|
|
9162
|
+
const bytes = await readFile(this.path);
|
|
9163
|
+
if (bytes.length > _GlobalInstructions.MAX_BYTES) {
|
|
9164
|
+
throw new Error(`Global instructions exceed ${_GlobalInstructions.MAX_BYTES} bytes: ${this.path}`);
|
|
9165
|
+
}
|
|
9166
|
+
const content = new TextDecoder("utf-8", { fatal: true }).decode(bytes).trim();
|
|
9167
|
+
this.#content = content || void 0;
|
|
9168
|
+
}
|
|
9169
|
+
async remember(input) {
|
|
9170
|
+
const rule = input.trim();
|
|
9171
|
+
if (!rule || /[\r\n]/u.test(rule)) throw new Error("Global rules must be one non-empty line.");
|
|
9172
|
+
if (Buffer.byteLength(rule, "utf8") > _GlobalInstructions.MAX_RULE_BYTES) {
|
|
9173
|
+
throw new Error(`Global rule exceeds ${_GlobalInstructions.MAX_RULE_BYTES} bytes.`);
|
|
9174
|
+
}
|
|
9175
|
+
let added = false;
|
|
9176
|
+
const next = await updatePlainTextFile(this.path, async (current) => {
|
|
9177
|
+
const exists2 = await regularFileExists(this.path);
|
|
9178
|
+
const base = exists2 ? current ?? "" : "";
|
|
9179
|
+
validateDocument(base);
|
|
9180
|
+
if (ruleLines(base).some((item) => item.text.toLocaleLowerCase() === rule.toLocaleLowerCase())) return base;
|
|
9181
|
+
const eol = base.includes("\r\n") ? "\r\n" : "\n";
|
|
9182
|
+
const nextContent = base.length === 0 ? `# Global instructions${eol}${eol}- ${rule}${eol}` : `${base}${base.endsWith(eol) ? "" : eol}${eol}- ${rule}${eol}`;
|
|
9183
|
+
validateDocument(nextContent);
|
|
9184
|
+
added = true;
|
|
9185
|
+
return nextContent;
|
|
9186
|
+
}, _GlobalInstructions.MAX_BYTES);
|
|
9187
|
+
this.#content = next?.trim() || void 0;
|
|
9188
|
+
return added ? "added" : "already-present";
|
|
9189
|
+
}
|
|
9190
|
+
async forget(input) {
|
|
9191
|
+
const query = input.trim().toLocaleLowerCase();
|
|
9192
|
+
if (!query || /[\r\n]/u.test(query)) throw new Error("Specify one global rule to forget.");
|
|
9193
|
+
if (!await regularFileExists(this.path)) return "not-found";
|
|
9194
|
+
let result = "not-found";
|
|
9195
|
+
const next = await updatePlainTextFile(this.path, async (current) => {
|
|
9196
|
+
if (!await regularFileExists(this.path)) return void 0;
|
|
9197
|
+
const base = current ?? "";
|
|
9198
|
+
validateDocument(base);
|
|
9199
|
+
const entries = ruleLines(base);
|
|
9200
|
+
const exact = entries.filter((entry) => entry.text.toLocaleLowerCase() === query);
|
|
9201
|
+
const matches = exact.length > 0 ? exact : entries.filter((entry) => entry.text.toLocaleLowerCase().includes(query));
|
|
9202
|
+
if (matches.length === 0) return base;
|
|
9203
|
+
if (matches.length > 1) {
|
|
9204
|
+
result = "ambiguous";
|
|
9205
|
+
return base;
|
|
9206
|
+
}
|
|
9207
|
+
const match = matches[0];
|
|
9208
|
+
result = "removed";
|
|
9209
|
+
const remaining = `${base.slice(0, match.start)}${base.slice(match.end)}`;
|
|
9210
|
+
return remaining.trim() === "# Global instructions" ? "" : remaining;
|
|
9211
|
+
}, _GlobalInstructions.MAX_BYTES);
|
|
9212
|
+
this.#content = next?.trim() || void 0;
|
|
9213
|
+
return result;
|
|
9214
|
+
}
|
|
9215
|
+
};
|
|
9216
|
+
function ruleLines(content) {
|
|
9217
|
+
return [...content.matchAll(/^[ \t]*[-*+][ \t]+([^\r\n]+)(?:\r?\n|$)/gmu)].map((match) => ({
|
|
9218
|
+
text: match[1].trim(),
|
|
9219
|
+
start: match.index,
|
|
9220
|
+
end: match.index + match[0].length
|
|
9221
|
+
}));
|
|
9222
|
+
}
|
|
9223
|
+
function validateDocument(content) {
|
|
9224
|
+
if (Buffer.byteLength(content, "utf8") > GlobalInstructions.MAX_BYTES) {
|
|
9225
|
+
throw new Error(`Global instructions exceed ${GlobalInstructions.MAX_BYTES} bytes.`);
|
|
9226
|
+
}
|
|
9227
|
+
return content;
|
|
9228
|
+
}
|
|
9229
|
+
async function regularFileExists(path2) {
|
|
9230
|
+
try {
|
|
9231
|
+
const info = await lstat(path2);
|
|
9232
|
+
if (!info.isFile()) throw new Error(`Global instructions must be a regular file: ${path2}`);
|
|
9233
|
+
if (info.size > GlobalInstructions.MAX_BYTES) {
|
|
9234
|
+
throw new Error(`Global instructions exceed ${GlobalInstructions.MAX_BYTES} bytes: ${path2}`);
|
|
9235
|
+
}
|
|
9236
|
+
return true;
|
|
9237
|
+
} catch (error) {
|
|
9238
|
+
if (isMissing(error)) return false;
|
|
9239
|
+
throw error;
|
|
9240
|
+
}
|
|
9241
|
+
}
|
|
9242
|
+
function isMissing(error) {
|
|
9243
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
9244
|
+
}
|
|
9245
|
+
|
|
9115
9246
|
// src/context/workspace-instructions.ts
|
|
9116
9247
|
import { createHash as createHash2 } from "crypto";
|
|
9117
|
-
import { lstat, readFile, realpath } from "fs/promises";
|
|
9248
|
+
import { lstat as lstat2, readFile as readFile2, realpath } from "fs/promises";
|
|
9118
9249
|
import { dirname, isAbsolute, relative, resolve, sep } from "path";
|
|
9119
9250
|
var NAMES = ["AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
9120
9251
|
var ROOT_NAMES = ["AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
@@ -9174,11 +9305,11 @@ var WorkspaceInstructions = class {
|
|
|
9174
9305
|
for (const name of names) {
|
|
9175
9306
|
const path2 = resolve(directory.path, name);
|
|
9176
9307
|
try {
|
|
9177
|
-
const info = await
|
|
9308
|
+
const info = await lstat2(path2);
|
|
9178
9309
|
if (!info.isFile() && !info.isSymbolicLink()) continue;
|
|
9179
9310
|
const physical = await realpath(path2);
|
|
9180
9311
|
if (!within(physicalRoot, physical)) continue;
|
|
9181
|
-
const bytes = await
|
|
9312
|
+
const bytes = await readFile2(path2);
|
|
9182
9313
|
const clipped = bytes.subarray(0, this.#maxFileBytes);
|
|
9183
9314
|
const suffix = bytes.length > clipped.length ? `
|
|
9184
9315
|
|
|
@@ -9217,7 +9348,7 @@ ${document.content}
|
|
|
9217
9348
|
};
|
|
9218
9349
|
async function directoryForPath(path2) {
|
|
9219
9350
|
try {
|
|
9220
|
-
return (await
|
|
9351
|
+
return (await lstat2(path2)).isDirectory() ? path2 : dirname(path2);
|
|
9221
9352
|
} catch {
|
|
9222
9353
|
return dirname(path2);
|
|
9223
9354
|
}
|
|
@@ -10013,6 +10144,12 @@ var AgentLoop = class {
|
|
|
10013
10144
|
}
|
|
10014
10145
|
}
|
|
10015
10146
|
async *#runInternal(request) {
|
|
10147
|
+
try {
|
|
10148
|
+
await this.#options.beforeModelCall?.(request.signal);
|
|
10149
|
+
} catch (error) {
|
|
10150
|
+
yield { type: "error", error: normalizeProviderError(error) };
|
|
10151
|
+
return;
|
|
10152
|
+
}
|
|
10016
10153
|
this.#options.context.refreshContextSources();
|
|
10017
10154
|
this.#options.context.append(request.initialUserMessage ?? { role: "user", content: request.prompt });
|
|
10018
10155
|
let totalInputTokens = 0;
|
|
@@ -10073,6 +10210,7 @@ var AgentLoop = class {
|
|
|
10073
10210
|
return;
|
|
10074
10211
|
}
|
|
10075
10212
|
try {
|
|
10213
|
+
await this.#options.beforeModelCall?.(request.signal);
|
|
10076
10214
|
if (await this.#options.context.prepareForModelCall(request.signal)) yield { type: "compacted" };
|
|
10077
10215
|
} catch (error) {
|
|
10078
10216
|
yield { type: "error", error: normalizeProviderError(error) };
|
|
@@ -11077,7 +11215,7 @@ function redactClassifierText(value) {
|
|
|
11077
11215
|
// src/tools/runtime.ts
|
|
11078
11216
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
11079
11217
|
import { mkdir, writeFile } from "fs/promises";
|
|
11080
|
-
import { basename, join, resolve as resolve3 } from "path";
|
|
11218
|
+
import { basename, join as join2, resolve as resolve3 } from "path";
|
|
11081
11219
|
|
|
11082
11220
|
// src/tools/types.ts
|
|
11083
11221
|
var TOOL_PRESENTATION = /* @__PURE__ */ Symbol("flavor.tool-presentation");
|
|
@@ -11410,9 +11548,9 @@ var ToolRuntime = class {
|
|
|
11410
11548
|
const reason = perToolExceeded && turnExceeded ? "per_tool_and_turn_limit" : perToolExceeded ? "per_tool_limit" : "turn_limit";
|
|
11411
11549
|
const preview = headTailPreview(serialized, previewChars);
|
|
11412
11550
|
this.#turnOutputChars += preview.length;
|
|
11413
|
-
const directory =
|
|
11551
|
+
const directory = join2(this.#workspace, ".flavor", "tool-results");
|
|
11414
11552
|
const filename2 = `${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-")}-${randomUUID2()}-${safeFilename(tool)}.txt`;
|
|
11415
|
-
const savedTo =
|
|
11553
|
+
const savedTo = join2(directory, filename2);
|
|
11416
11554
|
let storage;
|
|
11417
11555
|
try {
|
|
11418
11556
|
await mkdir(directory, { recursive: true });
|
|
@@ -11631,6 +11769,7 @@ var LocalHarness = class {
|
|
|
11631
11769
|
...this.#options.softLimitFactor === void 0 ? {} : { softLimitFactor: this.#options.softLimitFactor },
|
|
11632
11770
|
...this.#options.extendIterations === void 0 ? {} : { extendIterations: this.#options.extendIterations },
|
|
11633
11771
|
...isMain && this.#options.hasActiveProgress !== void 0 ? { hasActiveProgress: this.#options.hasActiveProgress } : {},
|
|
11772
|
+
...this.#options.beforeModelCall === void 0 ? {} : { beforeModelCall: this.#options.beforeModelCall },
|
|
11634
11773
|
...isMain && this.#options.hallucinationGuard !== void 0 ? { hallucinationGuard: this.#options.hallucinationGuard } : {},
|
|
11635
11774
|
...this.#options.modelJournal === void 0 ? {} : { modelJournal: this.#options.modelJournal }
|
|
11636
11775
|
});
|
|
@@ -11660,7 +11799,7 @@ function toModelTool(tool) {
|
|
|
11660
11799
|
// src/harness/journal.ts
|
|
11661
11800
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
11662
11801
|
import { closeSync, constants, existsSync as existsSync2, fsyncSync, mkdirSync, openSync, readFileSync, realpathSync as realpathSync2, renameSync, statSync, truncateSync, unlinkSync, writeSync } from "fs";
|
|
11663
|
-
import { join as
|
|
11802
|
+
import { join as join3, relative as relative3, resolve as resolve4, sep as sep3 } from "path";
|
|
11664
11803
|
|
|
11665
11804
|
// src/harness/invariants.ts
|
|
11666
11805
|
function harnessInvariantViolations(records) {
|
|
@@ -11749,14 +11888,14 @@ var HarnessJournal = class {
|
|
|
11749
11888
|
#records = [];
|
|
11750
11889
|
constructor(options) {
|
|
11751
11890
|
const workspace = resolve4(options.workspace);
|
|
11752
|
-
const directory =
|
|
11891
|
+
const directory = join3(workspace, ".flavor", "sessions");
|
|
11753
11892
|
assertSessionId(options.sessionId);
|
|
11754
11893
|
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
11755
11894
|
assertWithin(workspace, directory);
|
|
11756
11895
|
const canonicalWorkspace = realpathSync2.native(workspace);
|
|
11757
11896
|
const canonicalDirectory = realpathSync2.native(directory);
|
|
11758
11897
|
if (!isWithin2(canonicalWorkspace, canonicalDirectory)) throw new Error("Harness journal directory escapes the workspace");
|
|
11759
|
-
this.#path =
|
|
11898
|
+
this.#path = join3(canonicalDirectory, `${options.sessionId}.events.jsonl`);
|
|
11760
11899
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
11761
11900
|
this.#maxBytes = options.maxBytes ?? MAX_JOURNAL_BYTES;
|
|
11762
11901
|
if (!Number.isSafeInteger(this.#maxBytes) || this.#maxBytes < 1024) throw new Error("maxBytes must be an integer of at least 1024");
|
|
@@ -12398,7 +12537,7 @@ function createIncidentReporter(options) {
|
|
|
12398
12537
|
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
12399
12538
|
import { chmod, unlink } from "fs/promises";
|
|
12400
12539
|
import { tmpdir } from "os";
|
|
12401
|
-
import { join as
|
|
12540
|
+
import { join as join4 } from "path";
|
|
12402
12541
|
import { createServer } from "net";
|
|
12403
12542
|
var MAX_REQUEST_BYTES = 64 * 1024;
|
|
12404
12543
|
var SOCKET_TIMEOUT_MS = 1e4;
|
|
@@ -12406,7 +12545,7 @@ function islandControlEndpoint(sessionId, platform = process.platform) {
|
|
|
12406
12545
|
const digest = createHash4("sha256").update(`${process.pid}\0${sessionId}`).digest("hex").slice(0, 20);
|
|
12407
12546
|
if (platform === "win32") return `\\\\.\\pipe\\flavor-island-control-${digest}`;
|
|
12408
12547
|
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
12409
|
-
return
|
|
12548
|
+
return join4(tmpdir(), `flavor-island-control-${uid}-${digest}.sock`);
|
|
12410
12549
|
}
|
|
12411
12550
|
async function createIslandControlServer(options) {
|
|
12412
12551
|
const platform = options.platform ?? process.platform;
|
|
@@ -12533,17 +12672,17 @@ function closeServer(server) {
|
|
|
12533
12672
|
// src/evolve/service.ts
|
|
12534
12673
|
import { spawn as spawn2 } from "child_process";
|
|
12535
12674
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
12536
|
-
import { join as
|
|
12675
|
+
import { join as join7 } from "path";
|
|
12537
12676
|
|
|
12538
12677
|
// src/evolve/loader.ts
|
|
12539
12678
|
import { cp, mkdir as mkdir2, mkdtemp, readdir as readdir2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
12540
12679
|
import { tmpdir as tmpdir2 } from "os";
|
|
12541
|
-
import { join as
|
|
12680
|
+
import { join as join5, resolve as resolve6 } from "path";
|
|
12542
12681
|
|
|
12543
12682
|
// src/plugins/host.ts
|
|
12544
12683
|
import { createHash as createHash5 } from "crypto";
|
|
12545
12684
|
import { lstatSync, realpathSync as realpathSync3 } from "fs";
|
|
12546
|
-
import { lstat as
|
|
12685
|
+
import { lstat as lstat3, open, readFile as readFile3, readdir, realpath as realpath3, stat, writeFile as writeFile2 } from "fs/promises";
|
|
12547
12686
|
import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5, sep as sep4 } from "path";
|
|
12548
12687
|
import { pathToFileURL } from "url";
|
|
12549
12688
|
|
|
@@ -12857,7 +12996,7 @@ var PluginHost = class {
|
|
|
12857
12996
|
async #readCandidate(inputRoot, source, label = inputRoot) {
|
|
12858
12997
|
let diagnosticLabel = label;
|
|
12859
12998
|
try {
|
|
12860
|
-
const rootInfo = await
|
|
12999
|
+
const rootInfo = await lstat3(inputRoot);
|
|
12861
13000
|
if (!rootInfo.isDirectory() || rootInfo.isSymbolicLink()) throw new Error("Plugin root must be a static directory");
|
|
12862
13001
|
const root = await realpath3(inputRoot);
|
|
12863
13002
|
const rawManifest = await readManifest(resolve5(root, MANIFEST));
|
|
@@ -12867,7 +13006,7 @@ var PluginHost = class {
|
|
|
12867
13006
|
const manifest = PluginManifestSchema.parse(rawManifest);
|
|
12868
13007
|
const entry = resolveContained(root, manifest.main, "Plugin entry");
|
|
12869
13008
|
const entrySnapshot = await snapshotEntry(root, entry);
|
|
12870
|
-
const fingerprint2 = createHash5("sha256").update(JSON.stringify(manifest)).update(await
|
|
13009
|
+
const fingerprint2 = createHash5("sha256").update(JSON.stringify(manifest)).update(await readFile3(entrySnapshot.physicalPath)).digest("hex");
|
|
12871
13010
|
return { manifest, root, source, entry, entrySnapshot, fingerprint: fingerprint2 };
|
|
12872
13011
|
} catch (error) {
|
|
12873
13012
|
this.#diagnose(diagnosticLabel, error, inputRoot);
|
|
@@ -13114,7 +13253,7 @@ var PluginHost = class {
|
|
|
13114
13253
|
}
|
|
13115
13254
|
};
|
|
13116
13255
|
async function readManifest(path2) {
|
|
13117
|
-
const lexical = await
|
|
13256
|
+
const lexical = await lstat3(path2, { bigint: true });
|
|
13118
13257
|
if (!lexical.isFile() || lexical.isSymbolicLink()) throw new Error("Plugin manifest must be a static regular file");
|
|
13119
13258
|
const handle = await open(path2, "r");
|
|
13120
13259
|
try {
|
|
@@ -13131,7 +13270,7 @@ async function readManifest(path2) {
|
|
|
13131
13270
|
if (bytesRead > MAX_MANIFEST_BYTES) throw new Error(`Plugin manifest exceeds ${MAX_MANIFEST_BYTES} bytes`);
|
|
13132
13271
|
const after = fromStats(await handle.stat({ bigint: true }));
|
|
13133
13272
|
assertSnapshot(before, after, "Plugin manifest");
|
|
13134
|
-
const current = fromStats(await
|
|
13273
|
+
const current = fromStats(await lstat3(path2, { bigint: true }));
|
|
13135
13274
|
assertSnapshot(before, current, "Plugin manifest path");
|
|
13136
13275
|
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, bytesRead)));
|
|
13137
13276
|
} finally {
|
|
@@ -13139,7 +13278,7 @@ async function readManifest(path2) {
|
|
|
13139
13278
|
}
|
|
13140
13279
|
}
|
|
13141
13280
|
async function snapshotEntry(root, entry) {
|
|
13142
|
-
const lexicalStats = await
|
|
13281
|
+
const lexicalStats = await lstat3(entry, { bigint: true });
|
|
13143
13282
|
if (!lexicalStats.isFile() && !lexicalStats.isSymbolicLink()) throw new Error("Plugin entry must be a file");
|
|
13144
13283
|
const physicalPath = await realpath3(entry);
|
|
13145
13284
|
assertContained(root, physicalPath, "Plugin entry symlink");
|
|
@@ -13227,8 +13366,8 @@ function compare(left, right) {
|
|
|
13227
13366
|
}
|
|
13228
13367
|
|
|
13229
13368
|
// src/evolve/loader.ts
|
|
13230
|
-
var PLUGIN_DIR =
|
|
13231
|
-
var VERSIONS_DIR =
|
|
13369
|
+
var PLUGIN_DIR = join5(".flavor", "plugins");
|
|
13370
|
+
var VERSIONS_DIR = join5(".flavor", "plugins", ".versions");
|
|
13232
13371
|
function sanitizePluginName(tool) {
|
|
13233
13372
|
const base = String(tool ?? "fix").replace(/[^a-z0-9-]/gi, "-").toLowerCase();
|
|
13234
13373
|
return base.startsWith("fix-") ? base : `fix-${base}`;
|
|
@@ -13272,8 +13411,8 @@ export function activate(ctx) {
|
|
|
13272
13411
|
async function scaffoldFixPlugin(workspace, name) {
|
|
13273
13412
|
const dir = fixPluginDir(workspace, name);
|
|
13274
13413
|
await mkdir2(dir, { recursive: true });
|
|
13275
|
-
const manifestPath =
|
|
13276
|
-
const entryPath =
|
|
13414
|
+
const manifestPath = join5(dir, "flavor-plugin.json");
|
|
13415
|
+
const entryPath = join5(dir, "index.js");
|
|
13277
13416
|
try {
|
|
13278
13417
|
await writeFile3(manifestPath, SCAFFOLD_MANIFEST(name), { encoding: "utf8", flag: "wx" });
|
|
13279
13418
|
await writeFile3(entryPath, SCAFFOLD_ENTRY(name), { encoding: "utf8", flag: "wx" });
|
|
@@ -13293,9 +13432,9 @@ async function verifyFixPlugin(workspace, name) {
|
|
|
13293
13432
|
return { ok: false, provided: [], tools: [], commands: [], error: `Plugin "${name}" not found.` };
|
|
13294
13433
|
}
|
|
13295
13434
|
const collected = { tools: [], commands: [] };
|
|
13296
|
-
const sandbox = await mkdtemp(
|
|
13435
|
+
const sandbox = await mkdtemp(join5(tmpdir2(), "flavor-evolve-verify-"));
|
|
13297
13436
|
try {
|
|
13298
|
-
await cp(pluginRoot,
|
|
13437
|
+
await cp(pluginRoot, join5(sandbox, name), { recursive: true });
|
|
13299
13438
|
const host = new PluginHost({
|
|
13300
13439
|
projectPluginDirs: [sandbox],
|
|
13301
13440
|
sandbox: true,
|
|
@@ -13346,7 +13485,7 @@ async function verifyFixPlugin(workspace, name) {
|
|
|
13346
13485
|
}
|
|
13347
13486
|
async function snapshotFixPlugin(workspace, name) {
|
|
13348
13487
|
const pluginRoot = fixPluginDir(workspace, name);
|
|
13349
|
-
const snapshotRoot =
|
|
13488
|
+
const snapshotRoot = join5(versionsDirFor(workspace, name), timestamp());
|
|
13350
13489
|
await mkdir2(snapshotRoot, { recursive: true });
|
|
13351
13490
|
await cp(pluginRoot, snapshotRoot, { recursive: true });
|
|
13352
13491
|
return snapshotRoot;
|
|
@@ -13367,7 +13506,7 @@ async function revertFixPlugin(workspace, name) {
|
|
|
13367
13506
|
}
|
|
13368
13507
|
const snapshots = entries.filter((entry) => !entry.startsWith(".")).sort();
|
|
13369
13508
|
if (snapshots.length === 0) throw new Error(`Plugin "${name}" has no snapshot to restore.`);
|
|
13370
|
-
const latest =
|
|
13509
|
+
const latest = join5(versionsRoot, snapshots[snapshots.length - 1]);
|
|
13371
13510
|
await rm(pluginRoot, { recursive: true, force: true });
|
|
13372
13511
|
await mkdir2(pluginRoot, { recursive: true });
|
|
13373
13512
|
await cp(latest, pluginRoot, { recursive: true });
|
|
@@ -13376,8 +13515,8 @@ async function revertFixPlugin(workspace, name) {
|
|
|
13376
13515
|
|
|
13377
13516
|
// src/evolve/store.ts
|
|
13378
13517
|
import { createHash as createHash6 } from "crypto";
|
|
13379
|
-
import { appendFile, mkdir as mkdir3, readFile as
|
|
13380
|
-
import { dirname as dirname4, join as
|
|
13518
|
+
import { appendFile, mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile4 } from "fs/promises";
|
|
13519
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
13381
13520
|
function normalizeError(message2) {
|
|
13382
13521
|
return String(message2 ?? "").replace(/\s+/g, " ").replace(/"[^"]*"/g, '"\u2026"').replace(/`[^`]*`/g, '"\u2026"').trim().slice(0, 160);
|
|
13383
13522
|
}
|
|
@@ -13390,7 +13529,7 @@ function argKeys(args) {
|
|
|
13390
13529
|
}
|
|
13391
13530
|
async function readJsonLines(file) {
|
|
13392
13531
|
try {
|
|
13393
|
-
const text = await
|
|
13532
|
+
const text = await readFile4(file, "utf8");
|
|
13394
13533
|
const records = [];
|
|
13395
13534
|
for (const line of text.split("\n")) {
|
|
13396
13535
|
const trimmed = line.trim();
|
|
@@ -13413,7 +13552,7 @@ async function writeJsonLines(file, records) {
|
|
|
13413
13552
|
}
|
|
13414
13553
|
async function readJsonArray(file) {
|
|
13415
13554
|
try {
|
|
13416
|
-
const parsed = JSON.parse(await
|
|
13555
|
+
const parsed = JSON.parse(await readFile4(file, "utf8"));
|
|
13417
13556
|
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
13418
13557
|
} catch {
|
|
13419
13558
|
return [];
|
|
@@ -13421,7 +13560,7 @@ async function readJsonArray(file) {
|
|
|
13421
13560
|
}
|
|
13422
13561
|
async function readRules(file) {
|
|
13423
13562
|
try {
|
|
13424
|
-
const parsed = JSON.parse(await
|
|
13563
|
+
const parsed = JSON.parse(await readFile4(file, "utf8"));
|
|
13425
13564
|
if (!Array.isArray(parsed)) return [];
|
|
13426
13565
|
return parsed.filter((item) => item !== null && typeof item === "object").filter((item) => typeof item.id === "string" && typeof item.text === "string").map((item) => ({
|
|
13427
13566
|
id: item.id,
|
|
@@ -13450,12 +13589,12 @@ var EvolveStore = class {
|
|
|
13450
13589
|
#queue = Promise.resolve();
|
|
13451
13590
|
constructor(options) {
|
|
13452
13591
|
const { workspace, maxSignals = 400, maxRules = 20 } = options;
|
|
13453
|
-
this.dir =
|
|
13454
|
-
this.signalsFile =
|
|
13455
|
-
this.reflectionsFile =
|
|
13456
|
-
this.doneFile =
|
|
13457
|
-
this.verifiedFile =
|
|
13458
|
-
this.rulesFile =
|
|
13592
|
+
this.dir = join6(workspace, ".flavor", "evolve");
|
|
13593
|
+
this.signalsFile = join6(this.dir, "signals.jsonl");
|
|
13594
|
+
this.reflectionsFile = join6(this.dir, "reflections.jsonl");
|
|
13595
|
+
this.doneFile = join6(this.dir, "done.json");
|
|
13596
|
+
this.verifiedFile = join6(this.dir, "verified.json");
|
|
13597
|
+
this.rulesFile = join6(this.dir, "rules.json");
|
|
13459
13598
|
this.maxSignals = maxSignals;
|
|
13460
13599
|
this.maxRules = maxRules;
|
|
13461
13600
|
}
|
|
@@ -13998,7 +14137,7 @@ function createEvolveService(options) {
|
|
|
13998
14137
|
if (suggestion === void 0) return `No open suggestion with id "${suggestionId}". Use /evolve suggest to list them.`;
|
|
13999
14138
|
const name = sanitizePluginName(suggestion.tool);
|
|
14000
14139
|
const dir = await scaffoldFixPlugin(workspace, name);
|
|
14001
|
-
await writeFile5(
|
|
14140
|
+
await writeFile5(join7(dir, "PLAN.md"), buildPlan(suggestion, name, "See the repeated failure below; describe your fix in PLAN.md."), "utf8");
|
|
14002
14141
|
await snapshotFixPlugin(workspace, name);
|
|
14003
14142
|
return [
|
|
14004
14143
|
`suggestion ${suggestion.id}: ${suggestion.tool} x${suggestion.count} \u2014 ${suggestion.error}`,
|
|
@@ -14071,7 +14210,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
14071
14210
|
name: "evolve_improve",
|
|
14072
14211
|
description: "Implement a fix for one repeated tool failure. Default kind=plugin scaffolds the fix-<tool>/ plugin dir and writes PLAN.md with instructions for implementing, verifying, reloading, and testing it. kind=prompt_rule instead stores a concise guardrail rule (from `implementation`) that is injected into future system prompts and closes the suggestion. Use when the model proposes a concrete fix for a suggestion in the system prompt.",
|
|
14073
14212
|
inputSchema,
|
|
14074
|
-
paths: (input) => [
|
|
14213
|
+
paths: (input) => [join7(workspace, ".flavor", "evolve"), ...input.kind === "prompt_rule" ? [] : [join7(workspace, ".flavor", "plugins")]],
|
|
14075
14214
|
execute: async (input, signal) => {
|
|
14076
14215
|
signal.throwIfAborted();
|
|
14077
14216
|
const { suggestionId, implementation, kind } = input;
|
|
@@ -14091,7 +14230,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
14091
14230
|
}
|
|
14092
14231
|
const name = sanitizePluginName(suggestion.tool);
|
|
14093
14232
|
const dir = await scaffoldFixPlugin(workspace, name);
|
|
14094
|
-
await writeFile5(
|
|
14233
|
+
await writeFile5(join7(dir, "PLAN.md"), buildPlan(suggestion, name, implementation), "utf8");
|
|
14095
14234
|
await snapshotFixPlugin(workspace, name);
|
|
14096
14235
|
return [
|
|
14097
14236
|
`Scaffolded fix plugin at ${dir} for suggestion [${suggestion.id}] (${suggestion.tool} x${suggestion.count}).`,
|
|
@@ -14589,7 +14728,7 @@ ${cycle.verification.summary}` !== signature) break;
|
|
|
14589
14728
|
// src/goal/orchestrator.ts
|
|
14590
14729
|
import { createHash as createHash8 } from "crypto";
|
|
14591
14730
|
import { writeFile as writeFile6, mkdir as mkdir4 } from "fs/promises";
|
|
14592
|
-
import { join as
|
|
14731
|
+
import { join as join9 } from "path";
|
|
14593
14732
|
|
|
14594
14733
|
// src/goal/types.ts
|
|
14595
14734
|
var GoalPhaseSchema = external_exports.enum([
|
|
@@ -14759,8 +14898,8 @@ function extractJson(text) {
|
|
|
14759
14898
|
|
|
14760
14899
|
// src/goal/classifier.ts
|
|
14761
14900
|
import { createHash as createHash7 } from "crypto";
|
|
14762
|
-
import { readFile as
|
|
14763
|
-
import { join as
|
|
14901
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
14902
|
+
import { join as join8 } from "path";
|
|
14764
14903
|
function buildSkepticPrompt(evidence, plan, index) {
|
|
14765
14904
|
const criteriaList = plan.criteria.map((c) => ` ${c.id}. [${c.type}] ${c.description}`).join("\n");
|
|
14766
14905
|
return [
|
|
@@ -14939,10 +15078,10 @@ function computeFingerprint(gaps) {
|
|
|
14939
15078
|
}
|
|
14940
15079
|
async function collectEvidence(workspace, objective, finalResponse, priorGaps, contractHash2 = createHash7("sha256").update(objective).digest("hex"), hostVerification) {
|
|
14941
15080
|
const workspaceState = await collectWorkspaceState(workspace);
|
|
14942
|
-
const planPath =
|
|
15081
|
+
const planPath = join8(workspace, ".flavor", "goal-plan.md");
|
|
14943
15082
|
let planFile = null;
|
|
14944
15083
|
try {
|
|
14945
|
-
planFile = await
|
|
15084
|
+
planFile = await readFile5(planPath, "utf8");
|
|
14946
15085
|
} catch {
|
|
14947
15086
|
}
|
|
14948
15087
|
return {
|
|
@@ -15311,8 +15450,8 @@ function waitForStageRetry(delayMs, signal) {
|
|
|
15311
15450
|
});
|
|
15312
15451
|
}
|
|
15313
15452
|
async function writePlanFile(workspace, plan) {
|
|
15314
|
-
const dir =
|
|
15315
|
-
const planPath =
|
|
15453
|
+
const dir = join9(workspace, ".flavor");
|
|
15454
|
+
const planPath = join9(dir, "goal-plan.md");
|
|
15316
15455
|
await mkdir4(dir, { recursive: true });
|
|
15317
15456
|
const criteriaText = plan.criteria.map((c) => `- [${c.type}] AC-${c.id}: ${c.description}`).join("\n");
|
|
15318
15457
|
const content = [
|
|
@@ -15389,8 +15528,8 @@ function formatPriorGaps(gaps) {
|
|
|
15389
15528
|
// src/goal/store.ts
|
|
15390
15529
|
import { constants as constants2 } from "fs";
|
|
15391
15530
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
15392
|
-
import { lstat as
|
|
15393
|
-
import { dirname as dirname5, join as
|
|
15531
|
+
import { lstat as lstat4, mkdir as mkdir5, open as open2, readFile as readFile6, rename, rm as rm3, stat as stat3 } from "fs/promises";
|
|
15532
|
+
import { dirname as dirname5, join as join10, relative as relative5, resolve as resolve7, sep as sep5 } from "path";
|
|
15394
15533
|
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
15395
15534
|
var GoalStore = class {
|
|
15396
15535
|
#workspace;
|
|
@@ -15398,7 +15537,7 @@ var GoalStore = class {
|
|
|
15398
15537
|
#maxBytes;
|
|
15399
15538
|
constructor(options) {
|
|
15400
15539
|
this.#workspace = resolve7(options.workspace);
|
|
15401
|
-
this.#root =
|
|
15540
|
+
this.#root = join10(this.#workspace, ".flavor", "goals");
|
|
15402
15541
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
15403
15542
|
}
|
|
15404
15543
|
async save(input) {
|
|
@@ -15406,13 +15545,13 @@ var GoalStore = class {
|
|
|
15406
15545
|
await assertNoSymlink(this.#workspace, dirname5(this.#root));
|
|
15407
15546
|
await mkdir5(this.#root, { recursive: true, mode: 448 });
|
|
15408
15547
|
await assertNoSymlink(this.#workspace, this.#root);
|
|
15409
|
-
const target =
|
|
15548
|
+
const target = join10(this.#root, `${state.id}.json`);
|
|
15410
15549
|
const body = `${JSON.stringify(state, null, 2)}
|
|
15411
15550
|
`;
|
|
15412
15551
|
if (Buffer.byteLength(body) > this.#maxBytes) {
|
|
15413
15552
|
throw new Error(`Goal state exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
15414
15553
|
}
|
|
15415
|
-
const temporary =
|
|
15554
|
+
const temporary = join10(this.#root, `.${state.id}.${process.pid}.${randomUUID6()}.tmp`);
|
|
15416
15555
|
let handle;
|
|
15417
15556
|
try {
|
|
15418
15557
|
handle = await open2(temporary, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY, 384);
|
|
@@ -15433,13 +15572,13 @@ var GoalStore = class {
|
|
|
15433
15572
|
}
|
|
15434
15573
|
await assertNoSymlink(this.#workspace, dirname5(this.#root));
|
|
15435
15574
|
await assertNoSymlink(this.#workspace, this.#root);
|
|
15436
|
-
const target =
|
|
15575
|
+
const target = join10(this.#root, `${goalId}.json`);
|
|
15437
15576
|
try {
|
|
15438
15577
|
const metadata = await stat3(target);
|
|
15439
15578
|
if (!metadata.isFile() || metadata.size > this.#maxBytes) {
|
|
15440
15579
|
throw new Error("Goal state is not a bounded regular file");
|
|
15441
15580
|
}
|
|
15442
|
-
const raw = await
|
|
15581
|
+
const raw = await readFile6(target, "utf8");
|
|
15443
15582
|
return GoalStateSchema.parse(JSON.parse(raw));
|
|
15444
15583
|
} catch (error) {
|
|
15445
15584
|
if (isCode(error, "ENOENT")) throw new Error(`Goal "${goalId}" was not found`);
|
|
@@ -15463,7 +15602,7 @@ async function assertNoSymlink(root, target) {
|
|
|
15463
15602
|
}
|
|
15464
15603
|
for (const path2 of chain.reverse()) {
|
|
15465
15604
|
try {
|
|
15466
|
-
if ((await
|
|
15605
|
+
if ((await lstat4(path2)).isSymbolicLink()) throw new Error(`Goal path contains a symbolic link: ${path2}`);
|
|
15467
15606
|
} catch (error) {
|
|
15468
15607
|
if (!isCode(error, "ENOENT")) throw error;
|
|
15469
15608
|
}
|
|
@@ -15474,8 +15613,8 @@ function isCode(error, code) {
|
|
|
15474
15613
|
}
|
|
15475
15614
|
|
|
15476
15615
|
// src/loop/isolation.ts
|
|
15477
|
-
import { appendFile as appendFile2, mkdir as mkdir6, readFile as
|
|
15478
|
-
import { join as
|
|
15616
|
+
import { appendFile as appendFile2, mkdir as mkdir6, readFile as readFile7 } from "fs/promises";
|
|
15617
|
+
import { join as join11, resolve as resolve8 } from "path";
|
|
15479
15618
|
var CODE_CHANGE = /\b(?:fix|implement|add|update|change|modify|refactor|build|create|remove|delete|write|migrate)\b|修复|实现|添加|更新|修改|重构|创建|删除|迁移/i;
|
|
15480
15619
|
var READ_ONLY = /\b(?:review|analy[sz]e|inspect|explain|research|investigate|audit|summari[sz]e|report)\b|调研|分析|审查|检查|解释|研究|总结|报告/i;
|
|
15481
15620
|
function inferGoalIntent(goal) {
|
|
@@ -15498,8 +15637,8 @@ async function prepareLoopWorkspace(options) {
|
|
|
15498
15637
|
return { kind: "needs_human", reason: "The workspace has uncommitted changes that a new worktree would omit." };
|
|
15499
15638
|
}
|
|
15500
15639
|
options.signal?.throwIfAborted();
|
|
15501
|
-
const worktrees =
|
|
15502
|
-
const path2 =
|
|
15640
|
+
const worktrees = join11(root, ".worktrees");
|
|
15641
|
+
const path2 = join11(worktrees, `loop-${options.loopId}`);
|
|
15503
15642
|
const branch = `loop/${options.loopId}`;
|
|
15504
15643
|
try {
|
|
15505
15644
|
await ensureWorktreeDirectoryIgnored(root);
|
|
@@ -15529,12 +15668,12 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
15529
15668
|
const commonDirectory = await git(root, ["rev-parse", "--git-common-dir"]);
|
|
15530
15669
|
if (commonDirectory.code !== 0) throw new Error(commonDirectory.stderr.trim() || "Git common directory is unavailable");
|
|
15531
15670
|
const gitDirectory = resolve8(root, commonDirectory.stdout.trim());
|
|
15532
|
-
const infoDirectory =
|
|
15533
|
-
const excludePath =
|
|
15671
|
+
const infoDirectory = join11(gitDirectory, "info");
|
|
15672
|
+
const excludePath = join11(infoDirectory, "exclude");
|
|
15534
15673
|
await mkdir6(infoDirectory, { recursive: true });
|
|
15535
15674
|
let existing = "";
|
|
15536
15675
|
try {
|
|
15537
|
-
existing = await
|
|
15676
|
+
existing = await readFile7(excludePath, "utf8");
|
|
15538
15677
|
} catch (error) {
|
|
15539
15678
|
if (!(typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")) throw error;
|
|
15540
15679
|
}
|
|
@@ -15545,8 +15684,8 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
15545
15684
|
|
|
15546
15685
|
// src/loop/store.ts
|
|
15547
15686
|
import { constants as constants3 } from "fs";
|
|
15548
|
-
import { lstat as
|
|
15549
|
-
import { dirname as dirname6, join as
|
|
15687
|
+
import { lstat as lstat5, mkdir as mkdir7, open as open3, readFile as readFile8, realpath as realpath4, rename as rename2, rm as rm4, stat as stat4 } from "fs/promises";
|
|
15688
|
+
import { dirname as dirname6, join as join12, relative as relative6, resolve as resolve9, sep as sep6 } from "path";
|
|
15550
15689
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
15551
15690
|
var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
15552
15691
|
var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
|
|
@@ -15557,7 +15696,7 @@ var LoopStore = class {
|
|
|
15557
15696
|
#appendTails = /* @__PURE__ */ new Map();
|
|
15558
15697
|
constructor(options) {
|
|
15559
15698
|
this.#workspace = resolve9(options.workspace);
|
|
15560
|
-
this.#root =
|
|
15699
|
+
this.#root = join12(this.#workspace, ".flavor", "loops");
|
|
15561
15700
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES2;
|
|
15562
15701
|
}
|
|
15563
15702
|
async create(state) {
|
|
@@ -15574,11 +15713,11 @@ var LoopStore = class {
|
|
|
15574
15713
|
const state = LoopStateSchema.parse(input);
|
|
15575
15714
|
await this.#assertWorkspace(state.workspace);
|
|
15576
15715
|
const directory = await this.#prepare(state.loopId);
|
|
15577
|
-
const target =
|
|
15716
|
+
const target = join12(directory, "state.json");
|
|
15578
15717
|
const body = `${JSON.stringify(state, null, 2)}
|
|
15579
15718
|
`;
|
|
15580
15719
|
if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Loop state exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
15581
|
-
const temporary =
|
|
15720
|
+
const temporary = join12(directory, `.state.${process.pid}.${randomUUID7()}.tmp`);
|
|
15582
15721
|
let handle;
|
|
15583
15722
|
try {
|
|
15584
15723
|
handle = await open3(temporary, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
|
|
@@ -15594,11 +15733,11 @@ var LoopStore = class {
|
|
|
15594
15733
|
}
|
|
15595
15734
|
async load(loopId) {
|
|
15596
15735
|
const directory = await this.#safeExisting(loopId);
|
|
15597
|
-
const target =
|
|
15736
|
+
const target = join12(directory, "state.json");
|
|
15598
15737
|
try {
|
|
15599
15738
|
const metadata = await stat4(target);
|
|
15600
15739
|
if (!metadata.isFile() || metadata.size > this.#maxBytes) throw new Error("Loop state is not a bounded regular file");
|
|
15601
|
-
const raw = await
|
|
15740
|
+
const raw = await readFile8(target, "utf8");
|
|
15602
15741
|
const state = LoopStateSchema.parse(JSON.parse(raw));
|
|
15603
15742
|
await this.#assertWorkspace(state.workspace);
|
|
15604
15743
|
return state;
|
|
@@ -15614,7 +15753,7 @@ var LoopStore = class {
|
|
|
15614
15753
|
const previous = this.#appendTails.get(event.loopId) ?? Promise.resolve();
|
|
15615
15754
|
const next = previous.catch(() => void 0).then(async () => {
|
|
15616
15755
|
const directory = await this.#prepare(event.loopId);
|
|
15617
|
-
const path2 =
|
|
15756
|
+
const path2 = join12(directory, "events.jsonl");
|
|
15618
15757
|
const handle = await open3(path2, constants3.O_CREAT | constants3.O_APPEND | constants3.O_WRONLY, 384);
|
|
15619
15758
|
try {
|
|
15620
15759
|
await handle.writeFile(`${JSON.stringify(event)}
|
|
@@ -15631,7 +15770,7 @@ var LoopStore = class {
|
|
|
15631
15770
|
await assertNoSymlink2(this.#workspace, dirname6(this.#root));
|
|
15632
15771
|
await mkdir7(this.#root, { recursive: true, mode: 448 });
|
|
15633
15772
|
await assertNoSymlink2(this.#workspace, this.#root);
|
|
15634
|
-
const directory =
|
|
15773
|
+
const directory = join12(this.#root, loopId);
|
|
15635
15774
|
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
15636
15775
|
await assertNoSymlink2(this.#workspace, directory);
|
|
15637
15776
|
return directory;
|
|
@@ -15639,7 +15778,7 @@ var LoopStore = class {
|
|
|
15639
15778
|
async #safeExisting(loopId) {
|
|
15640
15779
|
validateId(loopId);
|
|
15641
15780
|
await assertNoSymlink2(this.#workspace, this.#root);
|
|
15642
|
-
const directory =
|
|
15781
|
+
const directory = join12(this.#root, loopId);
|
|
15643
15782
|
await assertNoSymlink2(this.#workspace, directory);
|
|
15644
15783
|
return directory;
|
|
15645
15784
|
}
|
|
@@ -15665,7 +15804,7 @@ async function assertNoSymlink2(root, target) {
|
|
|
15665
15804
|
}
|
|
15666
15805
|
for (const path2 of chain.reverse()) {
|
|
15667
15806
|
try {
|
|
15668
|
-
if ((await
|
|
15807
|
+
if ((await lstat5(path2)).isSymbolicLink()) throw new Error(`Loop path contains a symbolic link: ${path2}`);
|
|
15669
15808
|
} catch (error) {
|
|
15670
15809
|
if (!isCode2(error, "ENOENT")) throw error;
|
|
15671
15810
|
}
|
|
@@ -15687,20 +15826,20 @@ function isCode2(error, code) {
|
|
|
15687
15826
|
}
|
|
15688
15827
|
|
|
15689
15828
|
// src/loop/verifier.ts
|
|
15690
|
-
import { readFile as
|
|
15691
|
-
import { join as
|
|
15829
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
15830
|
+
import { join as join13 } from "path";
|
|
15692
15831
|
var SCRIPT_PRIORITY = ["test", "typecheck", "lint", "build", "smoke:install"];
|
|
15693
15832
|
var MAX_PERSISTED_VERIFICATION_STREAM_BYTES = 1024;
|
|
15694
15833
|
async function inferVerificationPlan(workspace) {
|
|
15695
15834
|
const commands = [];
|
|
15696
15835
|
const seen = /* @__PURE__ */ new Set();
|
|
15697
|
-
const packageJson = await optionalJson(
|
|
15836
|
+
const packageJson = await optionalJson(join13(workspace, "package.json"));
|
|
15698
15837
|
const scripts = isRecord(packageJson?.scripts) ? packageJson.scripts : {};
|
|
15699
15838
|
for (const name of SCRIPT_PRIORITY) {
|
|
15700
15839
|
if (typeof scripts[name] !== "string" || isUnconditionalPassThrough(scripts[name])) continue;
|
|
15701
15840
|
add(commands, seen, npmScript(name));
|
|
15702
15841
|
}
|
|
15703
|
-
const flavor = await optionalText(
|
|
15842
|
+
const flavor = await optionalText(join13(workspace, "FLAVOR.md"));
|
|
15704
15843
|
if (flavor !== void 0) {
|
|
15705
15844
|
for (const match of flavor.matchAll(/^- `([^`]+)`\s*$/gm)) {
|
|
15706
15845
|
const parsed = parseTrustedCommand(match[1] ?? "");
|
|
@@ -15775,7 +15914,7 @@ function isUnconditionalPassThrough(script) {
|
|
|
15775
15914
|
}
|
|
15776
15915
|
async function optionalText(path2) {
|
|
15777
15916
|
try {
|
|
15778
|
-
return await
|
|
15917
|
+
return await readFile9(path2, "utf8");
|
|
15779
15918
|
} catch (error) {
|
|
15780
15919
|
if (isCode3(error, "ENOENT")) return void 0;
|
|
15781
15920
|
throw error;
|
|
@@ -15795,7 +15934,7 @@ function isCode3(error, code) {
|
|
|
15795
15934
|
}
|
|
15796
15935
|
|
|
15797
15936
|
// src/models/anthropic.ts
|
|
15798
|
-
import { readFile as
|
|
15937
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
15799
15938
|
|
|
15800
15939
|
// src/utils/json.ts
|
|
15801
15940
|
function safeParseJson(input) {
|
|
@@ -16069,7 +16208,7 @@ ${modelContentText(message2.content)}
|
|
|
16069
16208
|
source: {
|
|
16070
16209
|
type: "base64",
|
|
16071
16210
|
media_type: block.mediaType,
|
|
16072
|
-
data: (await
|
|
16211
|
+
data: (await readFile10(block.source.path)).toString("base64")
|
|
16073
16212
|
}
|
|
16074
16213
|
}));
|
|
16075
16214
|
if (message2.cacheBreakpoint && Array.isArray(content) && content.length > 0) {
|
|
@@ -26604,7 +26743,7 @@ function isUndiciDispatcherVersionMismatchError(error) {
|
|
|
26604
26743
|
|
|
26605
26744
|
// src/models/openai.ts
|
|
26606
26745
|
import { createHash as createHash9 } from "crypto";
|
|
26607
|
-
import { readFile as
|
|
26746
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
26608
26747
|
var DEFAULT_THINKING_EFFORT = "high";
|
|
26609
26748
|
var EXPLICIT_CACHE_BREAKPOINT = { mode: "explicit" };
|
|
26610
26749
|
var MAX_EXPLICIT_CACHE_BREAKPOINTS = 3;
|
|
@@ -26627,7 +26766,7 @@ async function toInput(message2, cacheBreakpoint = false) {
|
|
|
26627
26766
|
prompt_cache_breakpoint: EXPLICIT_CACHE_BREAKPOINT
|
|
26628
26767
|
}] : message2.content : await Promise.all(message2.content.map(async (block) => block.type === "text" ? { type: "input_text", text: block.text } : {
|
|
26629
26768
|
type: "input_image",
|
|
26630
|
-
image_url: `data:${block.mediaType};base64,${(await
|
|
26769
|
+
image_url: `data:${block.mediaType};base64,${(await readFile11(block.source.path)).toString("base64")}`,
|
|
26631
26770
|
detail: "auto"
|
|
26632
26771
|
}));
|
|
26633
26772
|
if (cacheBreakpoint && Array.isArray(content) && content.length > 0) {
|
|
@@ -26871,16 +27010,28 @@ function selectExplicitBreakpointIndexes(messages) {
|
|
|
26871
27010
|
const indexes = messages.map((message2, index) => message2.cacheBreakpoint ? index : -1).filter((index) => index >= 0);
|
|
26872
27011
|
const tail = messages.length - 1;
|
|
26873
27012
|
if (tail >= 0 && indexes.at(-1) !== tail) indexes.push(tail);
|
|
26874
|
-
|
|
27013
|
+
const firstNonSystem = messages.findIndex((message2) => message2.role !== "system");
|
|
27014
|
+
const leadingSystemEnd = firstNonSystem < 0 ? tail : firstNonSystem - 1;
|
|
27015
|
+
const stable = indexes.filter((index) => index <= leadingSystemEnd).at(-1);
|
|
27016
|
+
if (stable === void 0) return new Set(indexes.slice(-MAX_EXPLICIT_CACHE_BREAKPOINTS));
|
|
27017
|
+
return /* @__PURE__ */ new Set([
|
|
27018
|
+
stable,
|
|
27019
|
+
...indexes.filter((index) => index !== stable).slice(-(MAX_EXPLICIT_CACHE_BREAKPOINTS - 1))
|
|
27020
|
+
]);
|
|
26875
27021
|
}
|
|
26876
27022
|
function promptCacheKey(request, sortedTools) {
|
|
26877
|
-
let stableEnd = -1;
|
|
26878
|
-
request.messages.forEach((message2, index) => {
|
|
26879
|
-
if (message2.cacheBreakpoint) stableEnd = index;
|
|
26880
|
-
});
|
|
26881
27023
|
const firstNonSystem = request.messages.findIndex((message2) => message2.role !== "system");
|
|
26882
27024
|
const leadingSystemEnd = firstNonSystem < 0 ? request.messages.length - 1 : firstNonSystem - 1;
|
|
26883
|
-
stableEnd =
|
|
27025
|
+
let stableEnd = -1;
|
|
27026
|
+
for (let index = 0; index <= leadingSystemEnd; index += 1) {
|
|
27027
|
+
if (request.messages[index]?.cacheBreakpoint) stableEnd = index;
|
|
27028
|
+
}
|
|
27029
|
+
if (stableEnd < 0) {
|
|
27030
|
+
request.messages.forEach((message2, index) => {
|
|
27031
|
+
if (message2.cacheBreakpoint) stableEnd = index;
|
|
27032
|
+
});
|
|
27033
|
+
stableEnd = Math.max(stableEnd, leadingSystemEnd);
|
|
27034
|
+
}
|
|
26884
27035
|
const stableMessages = stableEnd < 0 ? [] : request.messages.slice(0, stableEnd + 1);
|
|
26885
27036
|
const digest = createHash9("sha256").update(JSON.stringify({ model: request.model, messages: stableMessages, tools: sortedTools })).digest("hex").slice(0, 56);
|
|
26886
27037
|
return `flavor-${digest}`;
|
|
@@ -32531,8 +32682,8 @@ function isStoredToken(value) {
|
|
|
32531
32682
|
}
|
|
32532
32683
|
|
|
32533
32684
|
// src/permissions/policy.ts
|
|
32534
|
-
import { readFile as
|
|
32535
|
-
import { join as
|
|
32685
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
32686
|
+
import { join as join14, resolve as resolve11 } from "path";
|
|
32536
32687
|
var DecisionSchema2 = external_exports.enum(["allow", "ask", "deny"]);
|
|
32537
32688
|
var PrefixSchema = external_exports.array(external_exports.string().min(1).max(1024)).min(1).max(64);
|
|
32538
32689
|
var RuleSchema = external_exports.object({
|
|
@@ -32568,15 +32719,15 @@ var CompiledPermissionPolicy = class {
|
|
|
32568
32719
|
async function loadPermissionPolicy(options) {
|
|
32569
32720
|
const sources = [
|
|
32570
32721
|
...options.managedPath === void 0 ? [] : [{ tier: "managed", path: resolve11(options.managedPath) }],
|
|
32571
|
-
{ tier: "user", path:
|
|
32572
|
-
{ tier: "project", path:
|
|
32573
|
-
{ tier: "local", path:
|
|
32722
|
+
{ tier: "user", path: join14(resolve11(options.home), ".flavor-code", "permissions.json") },
|
|
32723
|
+
{ tier: "project", path: join14(resolve11(options.workspace), ".flavor", "permissions.json") },
|
|
32724
|
+
{ tier: "local", path: join14(resolve11(options.workspace), ".flavor", "permissions.local.json") }
|
|
32574
32725
|
];
|
|
32575
32726
|
const rules = [];
|
|
32576
32727
|
for (const source of sources) {
|
|
32577
32728
|
let raw;
|
|
32578
32729
|
try {
|
|
32579
|
-
raw = await
|
|
32730
|
+
raw = await readFile12(source.path, "utf8");
|
|
32580
32731
|
} catch (error) {
|
|
32581
32732
|
if (isCode4(error, "ENOENT")) continue;
|
|
32582
32733
|
throw error;
|
|
@@ -32907,14 +33058,14 @@ function createSkillResourceTool(registry) {
|
|
|
32907
33058
|
|
|
32908
33059
|
// src/session/tree.ts
|
|
32909
33060
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
32910
|
-
import { mkdir as mkdir9, readFile as
|
|
32911
|
-
import { dirname as dirname9, join as
|
|
33061
|
+
import { mkdir as mkdir9, readFile as readFile14, rename as rename4, writeFile as writeFile8 } from "fs/promises";
|
|
33062
|
+
import { dirname as dirname9, join as join16, resolve as resolve13 } from "path";
|
|
32912
33063
|
|
|
32913
33064
|
// src/session/checkpoint.ts
|
|
32914
33065
|
import { createHash as createHash13, randomUUID as randomUUID8 } from "crypto";
|
|
32915
33066
|
import { execFile } from "child_process";
|
|
32916
|
-
import { lstat as
|
|
32917
|
-
import { dirname as dirname8, join as
|
|
33067
|
+
import { lstat as lstat6, mkdir as mkdir8, open as open4, readFile as readFile13, readdir as readdir3, rename as rename3, rm as rm5, writeFile as writeFile7 } from "fs/promises";
|
|
33068
|
+
import { dirname as dirname8, join as join15, relative as relative7, resolve as resolve12, sep as sep7 } from "path";
|
|
32918
33069
|
var EXCLUDED = /* @__PURE__ */ new Set([".git", ".flavor", "node_modules", "dist", "build", "release", ".worktrees"]);
|
|
32919
33070
|
var MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
32920
33071
|
var MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
@@ -32927,9 +33078,9 @@ var WorkspaceCheckpointStore = class {
|
|
|
32927
33078
|
#maxTotalBytes;
|
|
32928
33079
|
constructor(options) {
|
|
32929
33080
|
this.#workspace = resolve12(options.workspace);
|
|
32930
|
-
this.#root =
|
|
32931
|
-
this.#objects =
|
|
32932
|
-
this.#manifests =
|
|
33081
|
+
this.#root = join15(this.#workspace, ".flavor", "checkpoints");
|
|
33082
|
+
this.#objects = join15(this.#root, "objects");
|
|
33083
|
+
this.#manifests = join15(this.#root, "manifests");
|
|
32933
33084
|
this.#maxFileBytes = options.maxFileBytes ?? MAX_FILE_BYTES;
|
|
32934
33085
|
this.#maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
|
|
32935
33086
|
}
|
|
@@ -32941,15 +33092,15 @@ var WorkspaceCheckpointStore = class {
|
|
|
32941
33092
|
await mkdir8(this.#manifests, { recursive: true });
|
|
32942
33093
|
for (const path2 of paths) {
|
|
32943
33094
|
const absolute = contained(this.#workspace, path2);
|
|
32944
|
-
const info = await
|
|
33095
|
+
const info = await lstat6(absolute);
|
|
32945
33096
|
if (info.isSymbolicLink()) throw new Error(`Checkpoint refuses symbolic link: ${path2}`);
|
|
32946
33097
|
if (!info.isFile()) continue;
|
|
32947
33098
|
if (info.size > this.#maxFileBytes) throw new Error(`Checkpoint file exceeds size limit: ${path2}`);
|
|
32948
33099
|
total += info.size;
|
|
32949
33100
|
if (total > this.#maxTotalBytes) throw new Error("Checkpoint exceeds total size limit");
|
|
32950
|
-
const content = await
|
|
33101
|
+
const content = await readFile13(absolute);
|
|
32951
33102
|
const digest = createHash13("sha256").update(content).digest("hex");
|
|
32952
|
-
const objectPath =
|
|
33103
|
+
const objectPath = join15(this.#objects, digest);
|
|
32953
33104
|
try {
|
|
32954
33105
|
const handle = await open4(objectPath, "wx");
|
|
32955
33106
|
try {
|
|
@@ -32969,12 +33120,12 @@ var WorkspaceCheckpointStore = class {
|
|
|
32969
33120
|
...label === void 0 || label.trim().length === 0 ? {} : { label: label.trim() },
|
|
32970
33121
|
files
|
|
32971
33122
|
};
|
|
32972
|
-
await atomicJson(
|
|
33123
|
+
await atomicJson(join15(this.#manifests, `${checkpoint.id}.json`), checkpoint);
|
|
32973
33124
|
return checkpoint;
|
|
32974
33125
|
}
|
|
32975
33126
|
async load(id) {
|
|
32976
33127
|
if (!/^checkpoint-[A-Za-z0-9-]+$/.test(id)) throw new Error("Invalid checkpoint id");
|
|
32977
|
-
const value = JSON.parse(await
|
|
33128
|
+
const value = JSON.parse(await readFile13(join15(this.#manifests, `${id}.json`), "utf8"));
|
|
32978
33129
|
if (value.version !== 1 || value.id !== id || !Array.isArray(value.files)) throw new Error("Invalid checkpoint manifest");
|
|
32979
33130
|
for (const file of value.files) {
|
|
32980
33131
|
contained(this.#workspace, file.path);
|
|
@@ -32986,7 +33137,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
32986
33137
|
const checkpoint = await this.load(id);
|
|
32987
33138
|
const objects = /* @__PURE__ */ new Map();
|
|
32988
33139
|
for (const file of checkpoint.files) {
|
|
32989
|
-
const object3 = await
|
|
33140
|
+
const object3 = await readFile13(join15(this.#objects, file.digest));
|
|
32990
33141
|
if (object3.length !== file.size || createHash13("sha256").update(object3).digest("hex") !== file.digest) {
|
|
32991
33142
|
throw new Error(`Checkpoint object is corrupt: ${file.digest}`);
|
|
32992
33143
|
}
|
|
@@ -33009,7 +33160,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
33009
33160
|
}
|
|
33010
33161
|
};
|
|
33011
33162
|
async function discoverFiles(workspace) {
|
|
33012
|
-
if (await exists(
|
|
33163
|
+
if (await exists(join15(workspace, ".git"))) {
|
|
33013
33164
|
const listed = await gitFiles(workspace);
|
|
33014
33165
|
return listed.filter((path2) => !EXCLUDED.has(path2.split("/")[0] ?? "")).sort();
|
|
33015
33166
|
}
|
|
@@ -33018,7 +33169,7 @@ async function discoverFiles(workspace) {
|
|
|
33018
33169
|
const entries = await readdir3(directory, { withFileTypes: true });
|
|
33019
33170
|
for (const entry of entries) {
|
|
33020
33171
|
if (EXCLUDED.has(entry.name)) continue;
|
|
33021
|
-
const absolute =
|
|
33172
|
+
const absolute = join15(directory, entry.name);
|
|
33022
33173
|
const path2 = normalizeRelative(relative7(workspace, absolute));
|
|
33023
33174
|
if (entry.isSymbolicLink()) throw new Error(`Checkpoint refuses symbolic link: ${path2}`);
|
|
33024
33175
|
if (entry.isDirectory()) await walk(absolute);
|
|
@@ -33052,7 +33203,7 @@ async function gitFiles(workspace) {
|
|
|
33052
33203
|
}
|
|
33053
33204
|
async function exists(path2) {
|
|
33054
33205
|
try {
|
|
33055
|
-
await
|
|
33206
|
+
await lstat6(path2);
|
|
33056
33207
|
return true;
|
|
33057
33208
|
} catch (error) {
|
|
33058
33209
|
if (isCode5(error, "ENOENT")) return false;
|
|
@@ -33093,17 +33244,17 @@ var SessionHistory = class _SessionHistory {
|
|
|
33093
33244
|
#leafId;
|
|
33094
33245
|
#unrevert;
|
|
33095
33246
|
constructor(options, document) {
|
|
33096
|
-
this.#path =
|
|
33247
|
+
this.#path = join16(resolve13(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
33097
33248
|
this.#checkpoints = new WorkspaceCheckpointStore({ workspace: options.workspace });
|
|
33098
33249
|
this.#restoreContext = options.restoreContext;
|
|
33099
33250
|
this.#nodes = boundedNodes(document.nodes);
|
|
33100
33251
|
this.#leafId = document.leafId !== null && this.#nodes.some((node) => node.id === document.leafId) ? document.leafId : this.#nodes.at(-1)?.id ?? null;
|
|
33101
33252
|
}
|
|
33102
33253
|
static async open(options) {
|
|
33103
|
-
const path2 =
|
|
33254
|
+
const path2 = join16(resolve13(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
33104
33255
|
let document = { version: 1, sessionId: options.sessionId, leafId: null, nodes: [] };
|
|
33105
33256
|
try {
|
|
33106
|
-
const parsed = JSON.parse(await
|
|
33257
|
+
const parsed = JSON.parse(await readFile14(path2, "utf8"));
|
|
33107
33258
|
validate(parsed, options.sessionId);
|
|
33108
33259
|
document = parsed;
|
|
33109
33260
|
} catch (error) {
|
|
@@ -33236,7 +33387,7 @@ function isCode6(error, code) {
|
|
|
33236
33387
|
|
|
33237
33388
|
// src/utils/rotation-census.ts
|
|
33238
33389
|
import { mkdirSync as mkdirSync2, readdirSync, unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
33239
|
-
import { join as
|
|
33390
|
+
import { join as join17 } from "path";
|
|
33240
33391
|
import { writeHeapSnapshot } from "v8";
|
|
33241
33392
|
var CENSUS_PREFIX = "rotation-census-";
|
|
33242
33393
|
var SNAPSHOT_PREFIX = "heap-";
|
|
@@ -33260,9 +33411,9 @@ function censusHeapBlock(reading) {
|
|
|
33260
33411
|
}
|
|
33261
33412
|
function writeRotationCensus(workspace, census) {
|
|
33262
33413
|
try {
|
|
33263
|
-
const directory =
|
|
33414
|
+
const directory = join17(workspace, ".flavor", "tmp");
|
|
33264
33415
|
mkdirSync2(directory, { recursive: true });
|
|
33265
|
-
const path2 =
|
|
33416
|
+
const path2 = join17(directory, `${CENSUS_PREFIX}${Date.now()}.json`);
|
|
33266
33417
|
writeFileSync(path2, `${JSON.stringify(census, null, 2)}
|
|
33267
33418
|
`, { encoding: "utf8", mode: 384 });
|
|
33268
33419
|
prune(directory, CENSUS_PREFIX, ".json", KEEP_CENSUS_FILES);
|
|
@@ -33273,9 +33424,9 @@ function writeRotationCensus(workspace, census) {
|
|
|
33273
33424
|
}
|
|
33274
33425
|
function writeRotationHeapSnapshot(workspace) {
|
|
33275
33426
|
try {
|
|
33276
|
-
const directory =
|
|
33427
|
+
const directory = join17(workspace, ".flavor", "tmp");
|
|
33277
33428
|
mkdirSync2(directory, { recursive: true });
|
|
33278
|
-
const path2 =
|
|
33429
|
+
const path2 = join17(directory, `${SNAPSHOT_PREFIX}${Date.now()}.heapsnapshot`);
|
|
33279
33430
|
writeHeapSnapshot(path2);
|
|
33280
33431
|
prune(directory, SNAPSHOT_PREFIX, ".heapsnapshot", KEEP_SNAPSHOT_FILES);
|
|
33281
33432
|
return path2;
|
|
@@ -33287,7 +33438,7 @@ function prune(directory, prefix, suffix, keep) {
|
|
|
33287
33438
|
const stale = readdirSync(directory).filter((name) => name.startsWith(prefix) && name.endsWith(suffix)).sort().slice(0, -keep);
|
|
33288
33439
|
for (const name of stale) {
|
|
33289
33440
|
try {
|
|
33290
|
-
unlinkSync2(
|
|
33441
|
+
unlinkSync2(join17(directory, name));
|
|
33291
33442
|
} catch {
|
|
33292
33443
|
}
|
|
33293
33444
|
}
|
|
@@ -33296,8 +33447,8 @@ function prune(directory, prefix, suffix, keep) {
|
|
|
33296
33447
|
// src/sleep/organizer.ts
|
|
33297
33448
|
import { constants as constants4 } from "fs";
|
|
33298
33449
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
33299
|
-
import { lstat as
|
|
33300
|
-
import { isAbsolute as isAbsolute4, join as
|
|
33450
|
+
import { lstat as lstat7, mkdir as mkdir10, open as open5, readdir as readdir4, realpath as realpath5, rename as rename5, rm as rm6 } from "fs/promises";
|
|
33451
|
+
import { isAbsolute as isAbsolute4, join as join18, relative as relative8, resolve as resolve14, sep as sep8 } from "path";
|
|
33301
33452
|
var DateKeySchema = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
33302
33453
|
var ReviewSchema = external_exports.object({
|
|
33303
33454
|
title: external_exports.string().min(1).max(120),
|
|
@@ -33351,7 +33502,7 @@ var ProjectSleepOrganizer = class {
|
|
|
33351
33502
|
#generate;
|
|
33352
33503
|
constructor(options) {
|
|
33353
33504
|
this.#workspace = resolve14(options.workspace);
|
|
33354
|
-
this.#sleepDirectory =
|
|
33505
|
+
this.#sleepDirectory = join18(this.#workspace, ".flavor", "sleep");
|
|
33355
33506
|
this.#sessions = options.sessions ?? new SessionStore({ workspace: this.#workspace });
|
|
33356
33507
|
this.#generate = options.generate;
|
|
33357
33508
|
}
|
|
@@ -33360,7 +33511,7 @@ var ProjectSleepOrganizer = class {
|
|
|
33360
33511
|
signal.throwIfAborted();
|
|
33361
33512
|
await this.#prepareDirectory();
|
|
33362
33513
|
if (await this.#hasReport(date)) return { status: "exists", date };
|
|
33363
|
-
const lockPath =
|
|
33514
|
+
const lockPath = join18(this.#sleepDirectory, `.${date}.lock`);
|
|
33364
33515
|
let lock;
|
|
33365
33516
|
try {
|
|
33366
33517
|
lock = await open5(lockPath, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY, 384);
|
|
@@ -33395,7 +33546,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
33395
33546
|
}
|
|
33396
33547
|
if (review === void 0) throw lastError;
|
|
33397
33548
|
const filename2 = `${date}-${filenameSummary(review.title)}.md`;
|
|
33398
|
-
const path2 =
|
|
33549
|
+
const path2 = join18(this.#sleepDirectory, filename2);
|
|
33399
33550
|
const markdown = renderSleepReport(date, review, documents);
|
|
33400
33551
|
await this.#atomicWrite(path2, markdown);
|
|
33401
33552
|
return { status: "written", date, path: path2, sessionCount: documents.length };
|
|
@@ -33437,7 +33588,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
33437
33588
|
});
|
|
33438
33589
|
}
|
|
33439
33590
|
async #prepareDirectory() {
|
|
33440
|
-
const flavorDirectory =
|
|
33591
|
+
const flavorDirectory = join18(this.#workspace, ".flavor");
|
|
33441
33592
|
await assertNotSymlink(flavorDirectory);
|
|
33442
33593
|
await mkdir10(flavorDirectory, { recursive: true, mode: 448 });
|
|
33443
33594
|
await assertNotSymlink(this.#sleepDirectory);
|
|
@@ -33451,7 +33602,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
33451
33602
|
return (await readdir4(this.#sleepDirectory, { withFileTypes: true })).some((entry) => entry.isFile() && entry.name.startsWith(prefix) && entry.name.endsWith(".md"));
|
|
33452
33603
|
}
|
|
33453
33604
|
async #atomicWrite(path2, content) {
|
|
33454
|
-
const temporary =
|
|
33605
|
+
const temporary = join18(this.#sleepDirectory, `.${randomUUID10()}.tmp`);
|
|
33455
33606
|
let handle;
|
|
33456
33607
|
try {
|
|
33457
33608
|
handle = await open5(temporary, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY, 384);
|
|
@@ -33767,7 +33918,7 @@ function assertDateKey(value) {
|
|
|
33767
33918
|
}
|
|
33768
33919
|
async function assertNotSymlink(path2) {
|
|
33769
33920
|
try {
|
|
33770
|
-
if ((await
|
|
33921
|
+
if ((await lstat7(path2)).isSymbolicLink()) throw new Error(`Refusing to use symbolic link: ${path2}`);
|
|
33771
33922
|
} catch (error) {
|
|
33772
33923
|
if (!hasCode(error, "ENOENT")) throw error;
|
|
33773
33924
|
}
|
|
@@ -33825,7 +33976,7 @@ function computeSessionStats(documents) {
|
|
|
33825
33976
|
|
|
33826
33977
|
// src/tools/files.ts
|
|
33827
33978
|
import { constants as constants5 } from "fs";
|
|
33828
|
-
import { mkdir as mkdir11, open as open6, readFile as
|
|
33979
|
+
import { mkdir as mkdir11, open as open6, readFile as readFile15, realpath as realpath6, rename as rename6, rm as rm7, stat as stat5 } from "fs/promises";
|
|
33829
33980
|
import { dirname as dirname10, isAbsolute as isAbsolute5, relative as relative9, resolve as resolve15, sep as sep9 } from "path";
|
|
33830
33981
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
33831
33982
|
|
|
@@ -34170,7 +34321,7 @@ function createPathGuard(workspace) {
|
|
|
34170
34321
|
if (!within2(physicalRoot, physical)) throw new Error("Path escapes the workspace through a symlink");
|
|
34171
34322
|
break;
|
|
34172
34323
|
} catch (error) {
|
|
34173
|
-
if (!
|
|
34324
|
+
if (!isMissing2(error)) throw error;
|
|
34174
34325
|
const parent = dirname10(ancestor);
|
|
34175
34326
|
if (parent === ancestor) throw error;
|
|
34176
34327
|
ancestor = parent;
|
|
@@ -34188,7 +34339,7 @@ async function atomicWrite(path2, content, signal, expectedVersion) {
|
|
|
34188
34339
|
try {
|
|
34189
34340
|
mode = (await stat5(path2)).mode & 511;
|
|
34190
34341
|
} catch (error) {
|
|
34191
|
-
if (!
|
|
34342
|
+
if (!isMissing2(error)) throw error;
|
|
34192
34343
|
}
|
|
34193
34344
|
try {
|
|
34194
34345
|
const handle = await open6(temporary, constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY, 384);
|
|
@@ -34209,7 +34360,7 @@ async function atomicWrite(path2, content, signal, expectedVersion) {
|
|
|
34209
34360
|
}
|
|
34210
34361
|
}
|
|
34211
34362
|
async function readText(path2) {
|
|
34212
|
-
const contents = await
|
|
34363
|
+
const contents = await readFile15(path2);
|
|
34213
34364
|
if (isBinary(contents)) throw new Error("Cannot edit binary file as text");
|
|
34214
34365
|
return decodeFileText(contents);
|
|
34215
34366
|
}
|
|
@@ -34237,7 +34388,7 @@ async function fileVersion(path2) {
|
|
|
34237
34388
|
try {
|
|
34238
34389
|
return versionOfStat(await stat5(path2));
|
|
34239
34390
|
} catch (error) {
|
|
34240
|
-
if (
|
|
34391
|
+
if (isMissing2(error)) return ABSENT_VERSION;
|
|
34241
34392
|
throw error;
|
|
34242
34393
|
}
|
|
34243
34394
|
}
|
|
@@ -34246,10 +34397,10 @@ function versionOfStat(info) {
|
|
|
34246
34397
|
}
|
|
34247
34398
|
async function readOptionalPresentationText(path2) {
|
|
34248
34399
|
try {
|
|
34249
|
-
const contents = await
|
|
34400
|
+
const contents = await readFile15(path2);
|
|
34250
34401
|
return isBinary(contents) ? { exists: true } : { exists: true, text: contents.toString("utf8") };
|
|
34251
34402
|
} catch (error) {
|
|
34252
|
-
return
|
|
34403
|
+
return isMissing2(error) ? { exists: false } : { exists: true };
|
|
34253
34404
|
}
|
|
34254
34405
|
}
|
|
34255
34406
|
function isBinary(contents) {
|
|
@@ -34358,7 +34509,7 @@ function mergeRange(ranges, start, end) {
|
|
|
34358
34509
|
function abortIfNeeded(signal) {
|
|
34359
34510
|
if (signal.aborted) throw signal.reason;
|
|
34360
34511
|
}
|
|
34361
|
-
function
|
|
34512
|
+
function isMissing2(error) {
|
|
34362
34513
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
34363
34514
|
}
|
|
34364
34515
|
var PATCH_SEARCH_RADIUS = 100;
|
|
@@ -34443,7 +34594,7 @@ async function requireAbsent(guard, path2) {
|
|
|
34443
34594
|
try {
|
|
34444
34595
|
await guard.existing(path2);
|
|
34445
34596
|
} catch (error) {
|
|
34446
|
-
if (
|
|
34597
|
+
if (isMissing2(error)) return "";
|
|
34447
34598
|
throw error;
|
|
34448
34599
|
}
|
|
34449
34600
|
throw new Error("Patch creation destination already exists");
|
|
@@ -35277,7 +35428,7 @@ async function readOptionalIgnoreFile(path2, maxBytes, signal) {
|
|
|
35277
35428
|
try {
|
|
35278
35429
|
handle = await open7(path2, "r");
|
|
35279
35430
|
} catch (error) {
|
|
35280
|
-
if (
|
|
35431
|
+
if (isMissing3(error)) return void 0;
|
|
35281
35432
|
throw error;
|
|
35282
35433
|
}
|
|
35283
35434
|
const buffer = Buffer.allocUnsafe(maxBytes + 1);
|
|
@@ -35295,7 +35446,7 @@ async function readOptionalIgnoreFile(path2, maxBytes, signal) {
|
|
|
35295
35446
|
if (offset > maxBytes) throw new Error(`Ignore file byte limit of ${maxBytes} exceeded: ${path2}`);
|
|
35296
35447
|
return buffer.subarray(0, offset);
|
|
35297
35448
|
}
|
|
35298
|
-
function
|
|
35449
|
+
function isMissing3(error) {
|
|
35299
35450
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
35300
35451
|
}
|
|
35301
35452
|
|
|
@@ -35628,7 +35779,7 @@ function charset(contentType) {
|
|
|
35628
35779
|
// src/tools/lsp.ts
|
|
35629
35780
|
import { spawn as spawn5 } from "child_process";
|
|
35630
35781
|
import { existsSync as existsSync3 } from "fs";
|
|
35631
|
-
import { access, constants as constants6, readFile as
|
|
35782
|
+
import { access, constants as constants6, readFile as readFile16 } from "fs/promises";
|
|
35632
35783
|
import { basename as basename2, dirname as dirname11, extname, isAbsolute as isAbsolute7, relative as relative11, resolve as resolve17, sep as sep11 } from "path";
|
|
35633
35784
|
import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "url";
|
|
35634
35785
|
var LSP_REQUEST_TIMEOUT_MS = 15e3;
|
|
@@ -35823,7 +35974,7 @@ var LspConnection = class {
|
|
|
35823
35974
|
const url2 = new URL(uri);
|
|
35824
35975
|
if (url2.protocol !== "file:") return;
|
|
35825
35976
|
const filePath = fileURLToPath(url2);
|
|
35826
|
-
const text = await
|
|
35977
|
+
const text = await readFile16(filePath, "utf8");
|
|
35827
35978
|
const current = this.#documents.get(uri);
|
|
35828
35979
|
if (current === void 0) {
|
|
35829
35980
|
this.#documents.set(uri, { text, version: 1 });
|
|
@@ -36556,12 +36707,12 @@ ${explanation}` : "";
|
|
|
36556
36707
|
// src/desktop/astgraph-service.ts
|
|
36557
36708
|
import { stat as stat7 } from "fs/promises";
|
|
36558
36709
|
import { createRequire as nodeCreateRequire } from "module";
|
|
36559
|
-
import { join as
|
|
36710
|
+
import { join as join19, resolve as resolve18 } from "path";
|
|
36560
36711
|
var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
|
|
36561
36712
|
var DesktopAstGraphService = class {
|
|
36562
36713
|
#path;
|
|
36563
36714
|
constructor(workspace) {
|
|
36564
|
-
this.#path =
|
|
36715
|
+
this.#path = join19(resolve18(workspace), ".flavor", "astgraph", "index.db");
|
|
36565
36716
|
}
|
|
36566
36717
|
async status() {
|
|
36567
36718
|
if ((await stat7(this.#path).catch(() => void 0))?.isFile() !== true) {
|
|
@@ -36841,8 +36992,8 @@ function createTodoWriteTool() {
|
|
|
36841
36992
|
|
|
36842
36993
|
// src/tools/managed.ts
|
|
36843
36994
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
36844
|
-
import { link, lstat as
|
|
36845
|
-
import { dirname as dirname12, join as
|
|
36995
|
+
import { link, lstat as lstat8, mkdir as mkdir12, readFile as readFile17, readdir as readdir5, unlink as unlink2, writeFile as writeFile9 } from "fs/promises";
|
|
36996
|
+
import { dirname as dirname12, join as join20, resolve as resolve20 } from "path";
|
|
36846
36997
|
var MANAGED_TOOL_VERSION = 1;
|
|
36847
36998
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
36848
36999
|
var TOOL_NAME = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
@@ -36893,8 +37044,8 @@ var ManagedToolStore = class {
|
|
|
36893
37044
|
#diagnostics = [];
|
|
36894
37045
|
constructor(options) {
|
|
36895
37046
|
this.#workspace = resolve20(options.workspace);
|
|
36896
|
-
this.#projectRoot =
|
|
36897
|
-
this.#globalRoot =
|
|
37047
|
+
this.#projectRoot = join20(this.#workspace, ".flavor", "tools");
|
|
37048
|
+
this.#globalRoot = join20(resolve20(options.home), ".flavor-code", "tools");
|
|
36898
37049
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
36899
37050
|
}
|
|
36900
37051
|
get diagnostics() {
|
|
@@ -36941,7 +37092,7 @@ var ManagedToolStore = class {
|
|
|
36941
37092
|
version: MANAGED_TOOL_VERSION,
|
|
36942
37093
|
createdAt: this.#now()
|
|
36943
37094
|
});
|
|
36944
|
-
const path2 =
|
|
37095
|
+
const path2 = join20(this.directory(input.scope), filename(input.name));
|
|
36945
37096
|
const definition = compileDefinition(record2, input.scope, this.#workspace);
|
|
36946
37097
|
await writeExclusive(path2, `${JSON.stringify(record2, void 0, 2)}
|
|
36947
37098
|
`);
|
|
@@ -36955,7 +37106,7 @@ var ManagedToolStore = class {
|
|
|
36955
37106
|
if (matches.length === 0) throw new Error(`Managed tool "${input.name}" was not found`);
|
|
36956
37107
|
if (matches.length > 1) throw new Error(`Managed tool "${input.name}" exists in multiple scopes; specify scope`);
|
|
36957
37108
|
const entry = matches[0];
|
|
36958
|
-
const info = await
|
|
37109
|
+
const info = await lstat8(entry.path);
|
|
36959
37110
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Managed tool path is no longer a regular file: ${entry.path}`);
|
|
36960
37111
|
await unlink2(entry.path);
|
|
36961
37112
|
this.#entries.delete(scopedKey(entry.scope, entry.record.name));
|
|
@@ -36966,7 +37117,7 @@ var ManagedToolStore = class {
|
|
|
36966
37117
|
if (!parsed.success) return [];
|
|
36967
37118
|
const matches = this.#matching(parsed.data.name).filter((entry) => parsed.data.scope === void 0 || entry.scope === parsed.data.scope);
|
|
36968
37119
|
if (matches.length > 0) return matches.map((entry) => entry.path);
|
|
36969
|
-
return [
|
|
37120
|
+
return [join20(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
|
|
36970
37121
|
}
|
|
36971
37122
|
async #loadDirectory(scope2) {
|
|
36972
37123
|
const root = this.directory(scope2);
|
|
@@ -36974,17 +37125,17 @@ var ManagedToolStore = class {
|
|
|
36974
37125
|
try {
|
|
36975
37126
|
names = (await readdir5(root, { withFileTypes: true })).filter((entry) => entry.name.endsWith(".json")).sort((left, right) => compare2(left.name, right.name)).map((entry) => entry.name);
|
|
36976
37127
|
} catch (error) {
|
|
36977
|
-
if (
|
|
37128
|
+
if (isMissing4(error)) return;
|
|
36978
37129
|
this.#diagnostics.push(`${root}: ${message(error)}`);
|
|
36979
37130
|
return;
|
|
36980
37131
|
}
|
|
36981
37132
|
for (const name of names) {
|
|
36982
|
-
const path2 =
|
|
37133
|
+
const path2 = join20(root, name);
|
|
36983
37134
|
try {
|
|
36984
|
-
const info = await
|
|
37135
|
+
const info = await lstat8(path2);
|
|
36985
37136
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
|
|
36986
37137
|
if (info.size > MAX_RECORD_BYTES) throw new Error(`Managed tool record exceeds ${MAX_RECORD_BYTES} bytes`);
|
|
36987
|
-
const record2 = ManagedToolRecordSchema.parse(JSON.parse(await
|
|
37138
|
+
const record2 = ManagedToolRecordSchema.parse(JSON.parse(await readFile17(path2, "utf8")));
|
|
36988
37139
|
if (record2.scope !== scope2) throw new Error(`Managed tool record scope must match its ${scope2} directory`);
|
|
36989
37140
|
const key = scopedKey(scope2, record2.name);
|
|
36990
37141
|
if (this.#entries.has(key)) throw new Error(`Duplicate managed tool name in ${scope2} scope: ${record2.name}`);
|
|
@@ -37177,7 +37328,7 @@ function compare2(left, right) {
|
|
|
37177
37328
|
function compareScope(left, right) {
|
|
37178
37329
|
return left === right ? 0 : left === "global" ? -1 : 1;
|
|
37179
37330
|
}
|
|
37180
|
-
function
|
|
37331
|
+
function isMissing4(error) {
|
|
37181
37332
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
37182
37333
|
}
|
|
37183
37334
|
|
|
@@ -37214,6 +37365,7 @@ var MVP_COMMANDS = [
|
|
|
37214
37365
|
"remember",
|
|
37215
37366
|
"forget",
|
|
37216
37367
|
"forget-cold",
|
|
37368
|
+
"global",
|
|
37217
37369
|
"checkpoint",
|
|
37218
37370
|
"tree",
|
|
37219
37371
|
"rewind",
|
|
@@ -37256,6 +37408,7 @@ var COMMAND_DESCRIPTIONS = {
|
|
|
37256
37408
|
remember: "Add a long-term memory",
|
|
37257
37409
|
forget: "Remove matching long-term memories",
|
|
37258
37410
|
"forget-cold": "Remove all cold long-term memories and their files",
|
|
37411
|
+
global: "Show or edit global coding instructions",
|
|
37259
37412
|
checkpoint: "Create a workspace and context checkpoint",
|
|
37260
37413
|
tree: "Show the session history tree",
|
|
37261
37414
|
rewind: "Restore a prior session node",
|
|
@@ -37354,6 +37507,13 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = [], mana
|
|
|
37354
37507
|
const query = args.join(" ").trim();
|
|
37355
37508
|
return query.length === 0 ? { name: "invalid", command: name, message: "Use /forget <text-or-id>." } : { name, query };
|
|
37356
37509
|
}
|
|
37510
|
+
if (name === "global") {
|
|
37511
|
+
const [action, ...parts] = args;
|
|
37512
|
+
if (action === void 0) return { name, action: "show" };
|
|
37513
|
+
const text = parts.join(" ").trim();
|
|
37514
|
+
if ((action === "remember" || action === "forget") && text.length > 0) return { name, action, text };
|
|
37515
|
+
return { name: "invalid", command: name, message: "Use /global [remember <rule>|forget <rule>]." };
|
|
37516
|
+
}
|
|
37357
37517
|
if (name === "checkpoint") {
|
|
37358
37518
|
const label = args.join(" ").trim();
|
|
37359
37519
|
return label.length === 0 ? { name } : { name, label };
|
|
@@ -37518,6 +37678,7 @@ var HELP = [
|
|
|
37518
37678
|
"/logout clear stored OAuth credentials",
|
|
37519
37679
|
"/init /config /skills /plugins /hooks /tasks /doctor",
|
|
37520
37680
|
"/memory /remember [type] <text> /forget <text-or-id> /forget-cold /finish",
|
|
37681
|
+
"/global /global remember <rule> /global forget <rule>",
|
|
37521
37682
|
"/checkpoint [label] /tree /rewind <node> /unrevert /fork <node>",
|
|
37522
37683
|
"/compact /clear /paste-image /help /exit",
|
|
37523
37684
|
"/loop <goal> run a verified autonomous loop",
|
|
@@ -37988,6 +38149,8 @@ Address this now (for example run the required verification or fix), then finish
|
|
|
37988
38149
|
this.#notice(await required(this.#services.ide, "ide")());
|
|
37989
38150
|
} else if (command.name === "memory") {
|
|
37990
38151
|
this.#notice(await this.#services.memory());
|
|
38152
|
+
} else if (command.name === "global") {
|
|
38153
|
+
this.#notice(await required(this.#services.global, "global")(command));
|
|
37991
38154
|
} else if (command.name === "remember") {
|
|
37992
38155
|
this.#notice(await this.#services.remember(command.type, command.text));
|
|
37993
38156
|
} else if (command.name === "forget") {
|
|
@@ -39333,8 +39496,8 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
39333
39496
|
}
|
|
39334
39497
|
|
|
39335
39498
|
// src/ide/client.ts
|
|
39336
|
-
import { readFile as
|
|
39337
|
-
import { basename as basename3, join as
|
|
39499
|
+
import { readFile as readFile18, readdir as readdir6, stat as stat8 } from "fs/promises";
|
|
39500
|
+
import { basename as basename3, join as join21, relative as relative14, resolve as resolve22, sep as sep14 } from "path";
|
|
39338
39501
|
var FlavorIdeClient = class {
|
|
39339
39502
|
#workspace;
|
|
39340
39503
|
#home;
|
|
@@ -39430,7 +39593,7 @@ var FlavorIdeClient = class {
|
|
|
39430
39593
|
};
|
|
39431
39594
|
if (await this.#healthy(configured)) return configured;
|
|
39432
39595
|
}
|
|
39433
|
-
const directory =
|
|
39596
|
+
const directory = join21(this.#home, ".flavor-code", "ide");
|
|
39434
39597
|
let entries;
|
|
39435
39598
|
try {
|
|
39436
39599
|
entries = (await readdir6(directory)).filter((name) => name.endsWith(".lock"));
|
|
@@ -39438,9 +39601,9 @@ var FlavorIdeClient = class {
|
|
|
39438
39601
|
return void 0;
|
|
39439
39602
|
}
|
|
39440
39603
|
const candidates = (await Promise.all(entries.map(async (name) => {
|
|
39441
|
-
const path2 =
|
|
39604
|
+
const path2 = join21(directory, name);
|
|
39442
39605
|
try {
|
|
39443
|
-
const [contents, info] = await Promise.all([
|
|
39606
|
+
const [contents, info] = await Promise.all([readFile18(path2, "utf8"), stat8(path2)]);
|
|
39444
39607
|
const lock = parseLockfile(contents);
|
|
39445
39608
|
return lock === void 0 ? void 0 : { lock, modified: info.mtimeMs };
|
|
39446
39609
|
} catch {
|
|
@@ -40633,6 +40796,8 @@ async function createProductionRuntime(options) {
|
|
|
40633
40796
|
const gitRepository = detectGitRepository(workspace);
|
|
40634
40797
|
const loaded = await loadConfig({ cwd: workspace, home, environment });
|
|
40635
40798
|
const config = loaded.config;
|
|
40799
|
+
const globalInstructions = config.globalInstructions.enabled ? new GlobalInstructions(home) : void 0;
|
|
40800
|
+
await globalInstructions?.refresh();
|
|
40636
40801
|
const mcpReady = connectMcpServers({
|
|
40637
40802
|
servers: config.mcpServers,
|
|
40638
40803
|
workspace,
|
|
@@ -40749,6 +40914,7 @@ async function createProductionRuntime(options) {
|
|
|
40749
40914
|
protocolVersion: 2,
|
|
40750
40915
|
workspace,
|
|
40751
40916
|
...hookSessionId === void 0 ? {} : { sessionId: hookSessionId },
|
|
40917
|
+
...hookSessionId === void 0 || options.islandSessionTitle === void 0 ? {} : { sessionTitle: options.islandSessionTitle(hookSessionId) },
|
|
40752
40918
|
...islandControlMetadata === void 0 ? {} : {
|
|
40753
40919
|
islandControlEndpoint: islandControlMetadata.endpoint,
|
|
40754
40920
|
islandControlToken: islandControlMetadata.token,
|
|
@@ -40767,6 +40933,18 @@ async function createProductionRuntime(options) {
|
|
|
40767
40933
|
);
|
|
40768
40934
|
const registry = new ModelRegistry();
|
|
40769
40935
|
const diagnostics = [];
|
|
40936
|
+
let lastGlobalInstructionError;
|
|
40937
|
+
const refreshGlobalInstructions = async () => {
|
|
40938
|
+
if (globalInstructions === void 0) return;
|
|
40939
|
+
try {
|
|
40940
|
+
await globalInstructions.refresh();
|
|
40941
|
+
lastGlobalInstructionError = void 0;
|
|
40942
|
+
} catch (error) {
|
|
40943
|
+
const diagnostic = `Global instructions could not be refreshed: ${message(error)}`;
|
|
40944
|
+
if (diagnostic !== lastGlobalInstructionError) diagnostics.push(diagnostic);
|
|
40945
|
+
lastGlobalInstructionError = diagnostic;
|
|
40946
|
+
}
|
|
40947
|
+
};
|
|
40770
40948
|
const permissionPolicy = await loadPermissionPolicy({
|
|
40771
40949
|
workspace,
|
|
40772
40950
|
home,
|
|
@@ -40870,8 +41048,8 @@ async function createProductionRuntime(options) {
|
|
|
40870
41048
|
const registeredProviders = registration.registered;
|
|
40871
41049
|
let effectiveLlm = registration.effectiveLlm;
|
|
40872
41050
|
const pluginHost = new PluginHost({
|
|
40873
|
-
globalPluginDirs: [
|
|
40874
|
-
projectPluginDirs: [
|
|
41051
|
+
globalPluginDirs: [join22(home, ".flavor-code", "plugins")],
|
|
41052
|
+
projectPluginDirs: [join22(workspace, ".flavor", "plugins")],
|
|
40875
41053
|
config,
|
|
40876
41054
|
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
40877
41055
|
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
@@ -40980,7 +41158,7 @@ async function createProductionRuntime(options) {
|
|
|
40980
41158
|
const address = palSocketAddress({
|
|
40981
41159
|
platform: process.platform,
|
|
40982
41160
|
userScope: home,
|
|
40983
|
-
runtimeDir:
|
|
41161
|
+
runtimeDir: join22(tmpdir3(), `flavor-code-pals-${createHash16("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
40984
41162
|
});
|
|
40985
41163
|
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
40986
41164
|
instanceId: collaboration.instanceId,
|
|
@@ -41091,8 +41269,8 @@ async function createProductionRuntime(options) {
|
|
|
41091
41269
|
diagnostics.push(`MCP servers could not start: ${message(error)}`);
|
|
41092
41270
|
});
|
|
41093
41271
|
const skills = new SkillRegistry({
|
|
41094
|
-
globalRoots: [
|
|
41095
|
-
projectRoots: [
|
|
41272
|
+
globalRoots: [join22(home, ".flavor-code", "skills")],
|
|
41273
|
+
projectRoots: [join22(workspace, ".flavor", "skills"), ...pluginSkillRoots],
|
|
41096
41274
|
authorizeResource: async () => true,
|
|
41097
41275
|
disabledNames: config.skills.disabled
|
|
41098
41276
|
});
|
|
@@ -41100,7 +41278,7 @@ async function createProductionRuntime(options) {
|
|
|
41100
41278
|
skillsReady.catch(() => void 0);
|
|
41101
41279
|
tools.push(createSkillTool(skills), createSkillResourceTool(skills));
|
|
41102
41280
|
if (options.extraTools !== void 0) tools.push(...options.extraTools);
|
|
41103
|
-
const flavor = await optionalText2(
|
|
41281
|
+
const flavor = await optionalText2(join22(workspace, "FLAVOR.md"));
|
|
41104
41282
|
const instructionBaseline = await workspaceInstructions.baseline();
|
|
41105
41283
|
let memoryContext;
|
|
41106
41284
|
if (memoryStore !== void 0) {
|
|
@@ -41340,6 +41518,7 @@ async function createProductionRuntime(options) {
|
|
|
41340
41518
|
...memoryContext === void 0 ? {} : { memory: memoryContext },
|
|
41341
41519
|
...taskState === void 0 ? {} : { taskState },
|
|
41342
41520
|
userMemory: () => userMemoryContext ?? "",
|
|
41521
|
+
...globalInstructions === void 0 ? {} : { globalInstructions: () => globalInstructions.content ?? "" },
|
|
41343
41522
|
...compactAtChars === void 0 ? {} : { compactAtChars },
|
|
41344
41523
|
toolOutputChars,
|
|
41345
41524
|
compaction,
|
|
@@ -41377,6 +41556,7 @@ async function createProductionRuntime(options) {
|
|
|
41377
41556
|
softLimitFactor: config.maxIterations.softLimitFactor,
|
|
41378
41557
|
extendIterations: config.maxIterations.extendBy,
|
|
41379
41558
|
hasActiveProgress,
|
|
41559
|
+
...globalInstructions === void 0 ? {} : { beforeModelCall: refreshGlobalInstructions },
|
|
41380
41560
|
afterToolSuccess: async (_tool, paths, _input, _output, context) => workspaceInstructions.discover(paths, context.ownerId ?? context.agent),
|
|
41381
41561
|
toolJournal: {
|
|
41382
41562
|
start: (tool, input, retrySafe) => harnessJournal.startTool(tool, input, retrySafe),
|
|
@@ -41697,7 +41877,7 @@ async function createProductionRuntime(options) {
|
|
|
41697
41877
|
evolveService.toolDefinition(),
|
|
41698
41878
|
...mcpTools
|
|
41699
41879
|
];
|
|
41700
|
-
const loopFlavor = await optionalText2(
|
|
41880
|
+
const loopFlavor = await optionalText2(join22(input.workspace, "FLAVOR.md"));
|
|
41701
41881
|
const loopInstructionBaseline = await loopInstructions.baseline();
|
|
41702
41882
|
const loopEnvironment = createPromptEnvironment({
|
|
41703
41883
|
now: /* @__PURE__ */ new Date(),
|
|
@@ -41737,6 +41917,7 @@ async function createProductionRuntime(options) {
|
|
|
41737
41917
|
...loopInstructionBaseline === "" ? {} : { workspaceInstructions: loopInstructionBaseline },
|
|
41738
41918
|
...memoryContext === void 0 ? {} : { memory: memoryContext },
|
|
41739
41919
|
userMemory: () => userMemoryContext ?? "",
|
|
41920
|
+
...globalInstructions === void 0 ? {} : { globalInstructions: () => globalInstructions.content ?? "" },
|
|
41740
41921
|
...compactAtChars === void 0 ? {} : { compactAtChars },
|
|
41741
41922
|
toolOutputChars,
|
|
41742
41923
|
compaction,
|
|
@@ -41770,6 +41951,7 @@ async function createProductionRuntime(options) {
|
|
|
41770
41951
|
softLimitFactor: config.maxIterations.softLimitFactor,
|
|
41771
41952
|
extendIterations: config.maxIterations.extendBy,
|
|
41772
41953
|
loopMode: true,
|
|
41954
|
+
...globalInstructions === void 0 ? {} : { beforeModelCall: refreshGlobalInstructions },
|
|
41773
41955
|
afterToolSuccess: async (_tool, paths, _input, _output, context) => loopInstructions.discover(paths, context.ownerId ?? context.agent),
|
|
41774
41956
|
toolJournal: {
|
|
41775
41957
|
start: (tool, input2, retrySafe) => harnessJournal.startTool(tool, input2, retrySafe),
|
|
@@ -42155,7 +42337,7 @@ async function createProductionRuntime(options) {
|
|
|
42155
42337
|
const root = resolve25(workspace);
|
|
42156
42338
|
const abs = resolve25(root, path2);
|
|
42157
42339
|
if (abs !== root && !abs.startsWith(root + sep16)) throw new Error(`path outside workspace: ${path2}`);
|
|
42158
|
-
return
|
|
42340
|
+
return readFile19(abs, "utf8");
|
|
42159
42341
|
},
|
|
42160
42342
|
history: async (path2, limit2) => fileHistory(workspace, path2, limit2),
|
|
42161
42343
|
questions,
|
|
@@ -42167,7 +42349,7 @@ async function createProductionRuntime(options) {
|
|
|
42167
42349
|
}, query, focus, signal),
|
|
42168
42350
|
audit: async (toolFilter) => {
|
|
42169
42351
|
try {
|
|
42170
|
-
const raw = await
|
|
42352
|
+
const raw = await readFile19(auditLogger.path, "utf8");
|
|
42171
42353
|
const lines = raw.trim().split("\n").filter((line) => line.length > 0);
|
|
42172
42354
|
const entries = lines.map((line) => {
|
|
42173
42355
|
try {
|
|
@@ -42207,7 +42389,7 @@ ${summary2}`;
|
|
|
42207
42389
|
doctor: async () => formatDoctorReport(await runDoctor({ workspace, home, environment })),
|
|
42208
42390
|
usage: async () => {
|
|
42209
42391
|
try {
|
|
42210
|
-
const raw = await
|
|
42392
|
+
const raw = await readFile19(usageLogPath(), "utf8");
|
|
42211
42393
|
return formatUsageSummary(summarizeUsage(parseUsageEntries(raw)));
|
|
42212
42394
|
} catch (error) {
|
|
42213
42395
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
@@ -42286,6 +42468,28 @@ ${summary2}`;
|
|
|
42286
42468
|
Path: ${memoryStore.path}` : `Path: ${memoryStore.path}
|
|
42287
42469
|
|
|
42288
42470
|
${renderMemoryDocument(entries)}`;
|
|
42471
|
+
},
|
|
42472
|
+
global: async (command) => {
|
|
42473
|
+
const path2 = join22(home, ".flavor-code", "GLOBAL.md");
|
|
42474
|
+
if (globalInstructions === void 0) return `Global instructions are disabled.
|
|
42475
|
+
Path: ${path2}`;
|
|
42476
|
+
if (command.action === "show") {
|
|
42477
|
+
await globalInstructions.refresh();
|
|
42478
|
+
return globalInstructions.content === void 0 ? `No global instructions stored.
|
|
42479
|
+
Path: ${path2}` : `Global instructions (enabled)
|
|
42480
|
+
Path: ${path2}
|
|
42481
|
+
|
|
42482
|
+
${globalInstructions.content}`;
|
|
42483
|
+
}
|
|
42484
|
+
if (command.action === "remember") {
|
|
42485
|
+
const result2 = await globalInstructions.remember(command.text);
|
|
42486
|
+
return result2 === "added" ? `Remembered global rule.
|
|
42487
|
+
Path: ${path2}` : `Global rule already exists.
|
|
42488
|
+
Path: ${path2}`;
|
|
42489
|
+
}
|
|
42490
|
+
const result = await globalInstructions.forget(command.text);
|
|
42491
|
+
return result === "removed" ? `Forgot global rule.
|
|
42492
|
+
Path: ${path2}` : result === "ambiguous" ? "Multiple global rules match. Use the full text of one rule." : "No matching global rule found.";
|
|
42289
42493
|
},
|
|
42290
42494
|
refreshMemory: refreshMemoryState,
|
|
42291
42495
|
remember: async (type, text) => {
|
|
@@ -42360,7 +42564,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
42360
42564
|
return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
|
|
42361
42565
|
}
|
|
42362
42566
|
try {
|
|
42363
|
-
const tokenStore = createFileTokenStore(
|
|
42567
|
+
const tokenStore = createFileTokenStore(join22(home, ".flavor-code", "auth.json"));
|
|
42364
42568
|
const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
|
|
42365
42569
|
await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
|
|
42366
42570
|
const oauth = new OAuthCallbackAuthProvider({
|
|
@@ -42408,7 +42612,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
42408
42612
|
}
|
|
42409
42613
|
},
|
|
42410
42614
|
async logout() {
|
|
42411
|
-
const tokenStore = createFileTokenStore(
|
|
42615
|
+
const tokenStore = createFileTokenStore(join22(home, ".flavor-code", "auth.json"));
|
|
42412
42616
|
const tokens = await tokenStore.load();
|
|
42413
42617
|
const hadCredentials = Object.keys(tokens).length > 0 || effectiveLlm !== void 0;
|
|
42414
42618
|
if (!hadCredentials) {
|
|
@@ -42719,7 +42923,7 @@ async function workspaceFingerprint(workspace) {
|
|
|
42719
42923
|
hash.update(name);
|
|
42720
42924
|
if (remaining <= 0) continue;
|
|
42721
42925
|
try {
|
|
42722
|
-
const content = await
|
|
42926
|
+
const content = await readFile19(path2);
|
|
42723
42927
|
const slice = content.subarray(0, remaining);
|
|
42724
42928
|
hash.update(slice);
|
|
42725
42929
|
remaining -= slice.length;
|
|
@@ -42990,7 +43194,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
42990
43194
|
const configured = { ...providers };
|
|
42991
43195
|
if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
|
|
42992
43196
|
if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
|
|
42993
|
-
const oauthTokenStore = createFileTokenStore(
|
|
43197
|
+
const oauthTokenStore = createFileTokenStore(join22(home, ".flavor-code", "auth.json"));
|
|
42994
43198
|
const registered = [];
|
|
42995
43199
|
let effectiveLlm;
|
|
42996
43200
|
for (const [name, provider] of Object.entries(configured)) {
|
|
@@ -43299,7 +43503,7 @@ async function generateSleepReview(registry, modelId, prompt, signal) {
|
|
|
43299
43503
|
}
|
|
43300
43504
|
async function optionalText2(path2) {
|
|
43301
43505
|
try {
|
|
43302
|
-
return await
|
|
43506
|
+
return await readFile19(path2, "utf8");
|
|
43303
43507
|
} catch (error) {
|
|
43304
43508
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return void 0;
|
|
43305
43509
|
throw error;
|