flavor-code 1.3.2 → 1.3.4
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 +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/{app-F6FEEF46.js → app-EHB3XAQY.js} +1 -1
- package/dist/{chunk-DFSMJ5SX.js → chunk-RDBTC35M.js} +1 -1
- package/dist/{chunk-Q5EQ4T3F.js → chunk-YKSMZDUZ.js} +149 -45
- package/dist/cli.js +3 -3
- package/dist/desktop/main.js +148 -44
- package/dist/desktop-renderer/assets/{index--vSisKu7.css → index-CdeAi527.css} +1 -1
- package/dist/desktop-renderer/assets/{index-ByWADjgv.js → index-bFGsR_uj.js} +5 -5
- package/dist/desktop-renderer/assets/{interactive-terminal-BAwIPsif.js → interactive-terminal-BwS6MvIe.js} +1 -1
- package/dist/desktop-renderer/index.html +2 -2
- package/dist/init/project.d.ts +2 -2
- package/dist/production.d.ts +2 -2
- package/dist/sdk/index.js +2 -2
- package/package.json +2 -2
- package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +2 -2
- package/dist/codeisland/activate.mjs +0 -74
- package/dist/codeisland/bridge.mjs +0 -97
- package/dist/codeisland/flavor-plugin.json +0 -27
package/README.md
CHANGED
|
@@ -252,10 +252,10 @@ flavor mcp disable docs
|
|
|
252
252
|
|
|
253
253
|
A Skill is a `SKILL.md` with YAML frontmatter, placed in `.flavor/skills/<name>/` or `~/.flavor-code/skills/<name>/`. Flavor loads skills progressively based on the task, and you can invoke one explicitly with `/<skill-name>`. Skill bodies support `$ARGUMENTS`, `$ARGUMENTS[N]`, and `$N` substitutions. A running composite Skill can load a dependency through the read-only `Skill` tool; plugin-qualified names such as `superharness:test-driven-development` resolve to discovered skills.
|
|
254
254
|
|
|
255
|
-
Plugins live in `.flavor/plugins/` and can register commands, tools, hooks, Skill roots, and model adapters. `additionalContext` returned by `SessionStart` and `UserPromptSubmit` hooks is added to the current task context, enabling reliable project-level engineering policy injection.
|
|
255
|
+
Plugins live in `.flavor/plugins/` and can register commands, tools, hooks, Skill roots, and model adapters. `additionalContext` returned by `SessionStart` and `UserPromptSubmit` hooks is added to the current task context, enabling reliable project-level engineering policy injection. Plugin loads record a content fingerprint plus declared capabilities. Worker/vm isolation is available through the embedding API's `pluginSandbox: true` option; the compatibility default remains in-process because bundled and existing plugins use Node.js APIs that the isolated runtime does not yet mediate.
|
|
256
256
|
|
|
257
257
|
> [!WARNING]
|
|
258
|
-
> Sandboxing reduces ambient access but does not make untrusted instructions safe
|
|
258
|
+
> The default in-process plugin runtime grants full Node.js access. Only install and enable plugins you trust. Sandboxing reduces ambient access but does not make untrusted instructions safe, and plugins that import Node.js built-ins will not load with `pluginSandbox: true` yet.
|
|
259
259
|
|
|
260
260
|
## Sessions, Memory & Execution Records
|
|
261
261
|
|
package/README.zh-CN.md
CHANGED
|
@@ -252,10 +252,10 @@ flavor mcp disable docs
|
|
|
252
252
|
|
|
253
253
|
Skill 是带有 YAML 头信息的 `SKILL.md`,放在 `.flavor/skills/<name>/` 或 `~/.flavor-code/skills/<name>/`。Flavor 会按任务渐进加载,也支持通过 `/<skill-name>` 显式调用。Skill 正文支持 `$ARGUMENTS`、`$ARGUMENTS[N]` 和 `$N` 参数占位符;运行中的组合 Skill 可以使用只读 `Skill` 工具继续加载依赖 Skill,插件限定名称(如 `superharness:test-driven-development`)会安全解析到已发现的 Skill。
|
|
254
254
|
|
|
255
|
-
插件放在 `.flavor/plugins/`,可以注册命令、工具、Hook、Skill 根目录和模型适配器。`SessionStart` 与 `UserPromptSubmit` Hook 返回的 `additionalContext`
|
|
255
|
+
插件放在 `.flavor/plugins/`,可以注册命令、工具、Hook、Skill 根目录和模型适配器。`SessionStart` 与 `UserPromptSubmit` Hook 返回的 `additionalContext` 会进入当前任务上下文,可用于注入项目级工程规则。插件加载会记录内容指纹与声明的能力。可通过嵌入 API 的 `pluginSandbox: true` 启用 Worker/vm 隔离;由于内置插件和已有插件依赖沙箱尚未代理的 Node.js API,当前兼容默认值仍为进程内运行。
|
|
256
256
|
|
|
257
257
|
> [!WARNING]
|
|
258
|
-
>
|
|
258
|
+
> 默认的进程内插件运行时拥有完整 Node.js 权限,只安装和启用你信任的插件。沙箱会降低环境权限,但不能让不可信指令自动变安全;目前导入 Node.js 内置模块的插件在 `pluginSandbox: true` 下仍无法加载。
|
|
259
259
|
|
|
260
260
|
## 会话、记忆与执行记录
|
|
261
261
|
|
|
@@ -561,7 +561,7 @@ function redactSecrets(input, secrets) {
|
|
|
561
561
|
|
|
562
562
|
// src/init/project.ts
|
|
563
563
|
import { lstat, mkdir, readdir, readFile, realpath, writeFile } from "fs/promises";
|
|
564
|
-
import { copyFileSync, mkdirSync, readdirSync
|
|
564
|
+
import { copyFileSync, mkdirSync, readdirSync } from "fs";
|
|
565
565
|
import { basename, dirname as dirname2, isAbsolute as isAbsolute2, join, relative as relative2, sep as sep2 } from "path";
|
|
566
566
|
import { fileURLToPath } from "url";
|
|
567
567
|
var GENERATED_START = "<!-- flavor-code:start -->";
|
|
@@ -1014,22 +1014,6 @@ async function createExampleFlavorConfig(cwd) {
|
|
|
1014
1014
|
};
|
|
1015
1015
|
await writeFile(configPath, JSON.stringify(example, null, 2) + "\n");
|
|
1016
1016
|
}
|
|
1017
|
-
var CODEISLAND_SRC_DIR = join(dirname2(fileURLToPath(import.meta.url)), "codeisland");
|
|
1018
|
-
var CODEISLAND_FILES = ["flavor-plugin.json", "activate.mjs", "bridge.mjs"];
|
|
1019
|
-
async function copyCodeIslandPlugin(cwd) {
|
|
1020
|
-
const pluginDir = join(cwd, ".flavor", "plugins", "codeisland");
|
|
1021
|
-
try {
|
|
1022
|
-
await lstat(pluginDir);
|
|
1023
|
-
return;
|
|
1024
|
-
} catch (error) {
|
|
1025
|
-
if (!isMissingPathError(error)) throw error;
|
|
1026
|
-
}
|
|
1027
|
-
await mkdir(pluginDir, { recursive: true, mode: 448 });
|
|
1028
|
-
for (const name of CODEISLAND_FILES) {
|
|
1029
|
-
const content = readFileSync(join(CODEISLAND_SRC_DIR, name), "utf8");
|
|
1030
|
-
await writeFile(join(pluginDir, name), content);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
1017
|
var ASTGRAPH_SRC_DIR = join(dirname2(fileURLToPath(import.meta.url)), "astgraph");
|
|
1034
1018
|
async function copyAstgraphPlugin(cwd) {
|
|
1035
1019
|
const pluginDir = join(cwd, ".flavor", "plugins", "astgraph");
|
|
@@ -1052,7 +1036,6 @@ function copyDirectory(from, to) {
|
|
|
1052
1036
|
}
|
|
1053
1037
|
async function initializeFlavor(cwd) {
|
|
1054
1038
|
await ensureFlavorDirectories(cwd);
|
|
1055
|
-
await copyCodeIslandPlugin(cwd);
|
|
1056
1039
|
await copyAstgraphPlugin(cwd);
|
|
1057
1040
|
await createExampleFlavorConfig(cwd);
|
|
1058
1041
|
const facts = await inspectProject(cwd);
|
|
@@ -1074,7 +1057,7 @@ async function initializeFlavor(cwd) {
|
|
|
1074
1057
|
}
|
|
1075
1058
|
|
|
1076
1059
|
// src/utils/version.ts
|
|
1077
|
-
import { readFileSync
|
|
1060
|
+
import { readFileSync } from "fs";
|
|
1078
1061
|
import { dirname as dirname3, join as join2 } from "path";
|
|
1079
1062
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1080
1063
|
var cached;
|
|
@@ -1083,7 +1066,7 @@ function packageVersion() {
|
|
|
1083
1066
|
let directory = dirname3(fileURLToPath2(import.meta.url));
|
|
1084
1067
|
for (; ; ) {
|
|
1085
1068
|
try {
|
|
1086
|
-
const manifest = JSON.parse(
|
|
1069
|
+
const manifest = JSON.parse(readFileSync(join2(directory, "package.json"), "utf8"));
|
|
1087
1070
|
if (typeof manifest.version === "string" && manifest.version.length > 0) {
|
|
1088
1071
|
cached = manifest.version;
|
|
1089
1072
|
return cached;
|
|
@@ -8002,7 +7985,7 @@ function toModelTool(tool) {
|
|
|
8002
7985
|
|
|
8003
7986
|
// src/harness/journal.ts
|
|
8004
7987
|
import { createHash as createHash6, randomUUID as randomUUID8 } from "crypto";
|
|
8005
|
-
import { closeSync, constants, existsSync as existsSync2, fsyncSync, mkdirSync as mkdirSync2, openSync, readFileSync as
|
|
7988
|
+
import { closeSync, constants, existsSync as existsSync2, fsyncSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, realpathSync as realpathSync2, renameSync, statSync, truncateSync, unlinkSync, writeSync } from "fs";
|
|
8006
7989
|
import { join as join8, relative as relative7, resolve as resolve8, sep as sep7 } from "path";
|
|
8007
7990
|
import { z as z10 } from "zod";
|
|
8008
7991
|
|
|
@@ -8013,8 +7996,10 @@ function harnessInvariantViolations(records) {
|
|
|
8013
7996
|
const tools = /* @__PURE__ */ new Set();
|
|
8014
7997
|
const models = /* @__PURE__ */ new Set();
|
|
8015
7998
|
const turns = /* @__PURE__ */ new Set();
|
|
7999
|
+
const firstSequence = records[0]?.sequence ?? 1;
|
|
8016
8000
|
records.forEach((record, index) => {
|
|
8017
|
-
|
|
8001
|
+
const expectedSequence = firstSequence + index;
|
|
8002
|
+
if (record.sequence !== expectedSequence) violations.push(`sequence ${record.sequence} is not ${expectedSequence}`);
|
|
8018
8003
|
const id = typeof record.payload.id === "string" ? record.payload.id : "";
|
|
8019
8004
|
if (record.type === "queue.admitted") {
|
|
8020
8005
|
if (queues.has(id)) violations.push(`queue ${id} was admitted twice`);
|
|
@@ -8030,13 +8015,13 @@ function harnessInvariantViolations(records) {
|
|
|
8030
8015
|
if (!tools.delete(id)) violations.push(`${record.type} references unknown tool ${id}`);
|
|
8031
8016
|
} else if (record.type === "model.requested") {
|
|
8032
8017
|
if (models.has(id)) violations.push(`model ${id} was requested twice`);
|
|
8033
|
-
if (!
|
|
8018
|
+
if (!isHash(record.payload.messagesHash)) violations.push(`model ${id} has no valid messages hash`);
|
|
8034
8019
|
models.add(id);
|
|
8035
8020
|
} else if (record.type === "model.completed") {
|
|
8036
8021
|
if (!models.delete(id)) violations.push(`model.completed references unknown model ${id}`);
|
|
8037
8022
|
} else if (record.type === "turn.started") {
|
|
8038
8023
|
if (turns.has(id)) violations.push(`turn ${id} was started twice`);
|
|
8039
|
-
if (!(
|
|
8024
|
+
if (!isHash(record.payload.promptHash)) violations.push(`turn ${id} has no valid prompt hash`);
|
|
8040
8025
|
turns.add(id);
|
|
8041
8026
|
} else if (record.type === "turn.completed" || record.type === "turn.interrupted") {
|
|
8042
8027
|
if (!turns.delete(id)) violations.push(`${record.type} references unknown turn ${id}`);
|
|
@@ -8044,6 +8029,9 @@ function harnessInvariantViolations(records) {
|
|
|
8044
8029
|
});
|
|
8045
8030
|
return violations;
|
|
8046
8031
|
}
|
|
8032
|
+
function isHash(value) {
|
|
8033
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
8034
|
+
}
|
|
8047
8035
|
function assertHarnessInvariants(records) {
|
|
8048
8036
|
const violations = harnessInvariantViolations(records);
|
|
8049
8037
|
if (violations.length > 0) throw new Error(`Harness invariant violation: ${violations.join("; ")}`);
|
|
@@ -8124,7 +8112,7 @@ var HarnessJournal = class {
|
|
|
8124
8112
|
}
|
|
8125
8113
|
startTurn(config, prompt) {
|
|
8126
8114
|
const id = randomUUID8();
|
|
8127
|
-
this.#append("turn.started", { id,
|
|
8115
|
+
this.#append("turn.started", { id, configHash: hashJson(config), promptHash: hashJson(prompt) });
|
|
8128
8116
|
return id;
|
|
8129
8117
|
}
|
|
8130
8118
|
completeTurn(id) {
|
|
@@ -8135,7 +8123,14 @@ var HarnessJournal = class {
|
|
|
8135
8123
|
}
|
|
8136
8124
|
startModel(input) {
|
|
8137
8125
|
const id = randomUUID8();
|
|
8138
|
-
this.#append("model.requested", {
|
|
8126
|
+
this.#append("model.requested", {
|
|
8127
|
+
id,
|
|
8128
|
+
agent: input.agent,
|
|
8129
|
+
model: input.model,
|
|
8130
|
+
iteration: input.iteration,
|
|
8131
|
+
attempt: input.attempt,
|
|
8132
|
+
messagesHash: hashJson(input.messages)
|
|
8133
|
+
});
|
|
8139
8134
|
return id;
|
|
8140
8135
|
}
|
|
8141
8136
|
completeModel(id, completed, error) {
|
|
@@ -8143,11 +8138,11 @@ var HarnessJournal = class {
|
|
|
8143
8138
|
}
|
|
8144
8139
|
startTool(tool, input, retrySafe) {
|
|
8145
8140
|
const id = randomUUID8();
|
|
8146
|
-
this.#append("tool.started", { id, tool,
|
|
8141
|
+
this.#append("tool.started", { id, tool, inputHash: hashJson(input), retrySafe });
|
|
8147
8142
|
return id;
|
|
8148
8143
|
}
|
|
8149
8144
|
completeTool(id, result) {
|
|
8150
|
-
this.#append("tool.completed", { id,
|
|
8145
|
+
this.#append("tool.completed", { id, resultHash: hashJson(result) });
|
|
8151
8146
|
}
|
|
8152
8147
|
interruptTool(id, reason) {
|
|
8153
8148
|
this.#append("tool.interrupted", { id, reason });
|
|
@@ -8220,51 +8215,157 @@ var HarnessJournal = class {
|
|
|
8220
8215
|
const metadata = statSync(this.#path);
|
|
8221
8216
|
if (!metadata.isFile()) throw new Error("Harness journal path is not a regular file");
|
|
8222
8217
|
if (metadata.size > this.#maxBytes) throw new Error(`Harness journal exceeds ${this.#maxBytes} bytes`);
|
|
8223
|
-
const raw =
|
|
8218
|
+
const raw = readFileSync2(this.#path, "utf8");
|
|
8224
8219
|
const committed = raw.endsWith("\n") ? raw : raw.slice(0, Math.max(0, raw.lastIndexOf("\n") + 1));
|
|
8225
8220
|
if (committed.length !== raw.length) truncateSync(this.#path, Buffer.byteLength(committed));
|
|
8226
8221
|
const lines = committed.split("\n").filter(Boolean);
|
|
8227
|
-
let previousHash
|
|
8222
|
+
let previousHash;
|
|
8223
|
+
let expectedSequence;
|
|
8228
8224
|
for (const line of lines) {
|
|
8229
8225
|
const record = JournalRecordSchema.parse(JSON.parse(line));
|
|
8230
|
-
|
|
8226
|
+
expectedSequence ??= record.sequence;
|
|
8227
|
+
previousHash ??= record.previousHash;
|
|
8228
|
+
if (record.sequence !== expectedSequence) throw new Error("Harness journal sequence is not contiguous");
|
|
8231
8229
|
if (record.previousHash !== previousHash) throw new Error("Harness journal hash chain is broken");
|
|
8232
8230
|
if (record.hash !== recordHash(record)) throw new Error("Harness journal record hash is invalid");
|
|
8233
8231
|
this.#records.push(record);
|
|
8234
8232
|
previousHash = record.hash;
|
|
8233
|
+
expectedSequence += 1;
|
|
8235
8234
|
}
|
|
8236
|
-
this.#sequence = this.#records.
|
|
8237
|
-
this.#lastHash = previousHash;
|
|
8235
|
+
this.#sequence = this.#records.at(-1)?.sequence ?? 0;
|
|
8236
|
+
this.#lastHash = previousHash ?? null;
|
|
8238
8237
|
assertHarnessInvariants(this.#records);
|
|
8238
|
+
const hasRotatedPrefix = (this.#records[0]?.sequence ?? 1) !== 1;
|
|
8239
|
+
if (metadata.size > this.#maxBytes * 0.75 || hasRotatedPrefix || this.#records.some(hasLegacyPayload)) this.#compact();
|
|
8239
8240
|
}
|
|
8240
8241
|
#append(type, payload) {
|
|
8241
8242
|
const normalizedPayload = jsonSafeRecord(payload);
|
|
8243
|
+
let record = this.#createRecord(type, normalizedPayload);
|
|
8244
|
+
let line = `${JSON.stringify(record)}
|
|
8245
|
+
`;
|
|
8246
|
+
let priorBytes = existsSync2(this.#path) ? statSync(this.#path).size : 0;
|
|
8247
|
+
if (priorBytes + Buffer.byteLength(line) > this.#maxBytes) {
|
|
8248
|
+
this.#compact();
|
|
8249
|
+
record = this.#createRecord(type, normalizedPayload);
|
|
8250
|
+
line = `${JSON.stringify(record)}
|
|
8251
|
+
`;
|
|
8252
|
+
priorBytes = existsSync2(this.#path) ? statSync(this.#path).size : 0;
|
|
8253
|
+
if (priorBytes + Buffer.byteLength(line) > this.#maxBytes) {
|
|
8254
|
+
throw new Error(`Harness journal event exceeds the ${this.#maxBytes} byte bounded journal capacity`);
|
|
8255
|
+
}
|
|
8256
|
+
}
|
|
8257
|
+
const fd = openSync(this.#path, constants.O_CREAT | constants.O_APPEND | constants.O_WRONLY, 384);
|
|
8258
|
+
try {
|
|
8259
|
+
writeSync(fd, line, void 0, "utf8");
|
|
8260
|
+
fsyncSync(fd);
|
|
8261
|
+
} finally {
|
|
8262
|
+
closeSync(fd);
|
|
8263
|
+
}
|
|
8264
|
+
this.#records.push(record);
|
|
8265
|
+
this.#sequence = record.sequence;
|
|
8266
|
+
this.#lastHash = record.hash;
|
|
8267
|
+
}
|
|
8268
|
+
#createRecord(type, payload) {
|
|
8242
8269
|
const unsigned = {
|
|
8243
8270
|
version: HARNESS_JOURNAL_VERSION,
|
|
8244
8271
|
sequence: this.#sequence + 1,
|
|
8245
8272
|
id: randomUUID8(),
|
|
8246
8273
|
timestamp: this.#now(),
|
|
8247
8274
|
type,
|
|
8248
|
-
payload
|
|
8275
|
+
payload,
|
|
8249
8276
|
previousHash: this.#lastHash
|
|
8250
8277
|
};
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
const
|
|
8255
|
-
|
|
8256
|
-
const
|
|
8278
|
+
return JournalRecordSchema.parse({ ...unsigned, hash: hashJson(unsigned) });
|
|
8279
|
+
}
|
|
8280
|
+
#compact() {
|
|
8281
|
+
const queues = /* @__PURE__ */ new Map();
|
|
8282
|
+
const claims = /* @__PURE__ */ new Map();
|
|
8283
|
+
const tools = /* @__PURE__ */ new Map();
|
|
8284
|
+
const models = /* @__PURE__ */ new Map();
|
|
8285
|
+
const turns = /* @__PURE__ */ new Map();
|
|
8286
|
+
let savepoint;
|
|
8287
|
+
for (const record of this.#records) {
|
|
8288
|
+
const id = typeof record.payload.id === "string" ? record.payload.id : "";
|
|
8289
|
+
if (record.type === "queue.admitted") queues.set(id, record);
|
|
8290
|
+
else if (record.type === "queue.claimed") claims.set(id, record);
|
|
8291
|
+
else if (record.type === "queue.released") claims.delete(id);
|
|
8292
|
+
else if (record.type === "queue.acked") {
|
|
8293
|
+
queues.delete(id);
|
|
8294
|
+
claims.delete(id);
|
|
8295
|
+
} else if (record.type === "tool.started") tools.set(id, record);
|
|
8296
|
+
else if (record.type === "tool.completed" || record.type === "tool.interrupted") tools.delete(id);
|
|
8297
|
+
else if (record.type === "model.requested") models.set(id, record);
|
|
8298
|
+
else if (record.type === "model.completed") models.delete(id);
|
|
8299
|
+
else if (record.type === "turn.started") turns.set(id, record);
|
|
8300
|
+
else if (record.type === "turn.completed" || record.type === "turn.interrupted") turns.delete(id);
|
|
8301
|
+
else if (record.type === "savepoint.created") savepoint = record;
|
|
8302
|
+
}
|
|
8303
|
+
const retained = [
|
|
8304
|
+
...queues.values(),
|
|
8305
|
+
...claims.values(),
|
|
8306
|
+
...tools.values(),
|
|
8307
|
+
...models.values(),
|
|
8308
|
+
...turns.values(),
|
|
8309
|
+
...savepoint === void 0 ? [] : [savepoint]
|
|
8310
|
+
].sort((left, right) => left.sequence - right.sequence);
|
|
8311
|
+
const compacted = [];
|
|
8312
|
+
let previousHash = null;
|
|
8313
|
+
for (const source of retained) {
|
|
8314
|
+
const unsigned = {
|
|
8315
|
+
version: HARNESS_JOURNAL_VERSION,
|
|
8316
|
+
sequence: compacted.length + 1,
|
|
8317
|
+
id: randomUUID8(),
|
|
8318
|
+
timestamp: source.timestamp,
|
|
8319
|
+
type: source.type,
|
|
8320
|
+
payload: compactPayload(source),
|
|
8321
|
+
previousHash
|
|
8322
|
+
};
|
|
8323
|
+
const record = JournalRecordSchema.parse({ ...unsigned, hash: hashJson(unsigned) });
|
|
8324
|
+
compacted.push(record);
|
|
8325
|
+
previousHash = record.hash;
|
|
8326
|
+
}
|
|
8327
|
+
const contents = compacted.map((record) => `${JSON.stringify(record)}
|
|
8328
|
+
`).join("");
|
|
8329
|
+
if (Buffer.byteLength(contents) > this.#maxBytes) return;
|
|
8330
|
+
const temporary = `${this.#path}.compact-${process.pid}-${randomUUID8()}`;
|
|
8331
|
+
const fd = openSync(temporary, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 384);
|
|
8257
8332
|
try {
|
|
8258
|
-
writeSync(fd,
|
|
8333
|
+
writeSync(fd, contents, void 0, "utf8");
|
|
8259
8334
|
fsyncSync(fd);
|
|
8260
8335
|
} finally {
|
|
8261
8336
|
closeSync(fd);
|
|
8262
8337
|
}
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8338
|
+
try {
|
|
8339
|
+
renameSync(temporary, this.#path);
|
|
8340
|
+
} catch (error) {
|
|
8341
|
+
if (existsSync2(temporary)) unlinkSync(temporary);
|
|
8342
|
+
throw error;
|
|
8343
|
+
}
|
|
8344
|
+
this.#records = compacted;
|
|
8345
|
+
this.#sequence = compacted.length;
|
|
8346
|
+
this.#lastHash = previousHash;
|
|
8266
8347
|
}
|
|
8267
8348
|
};
|
|
8349
|
+
function compactPayload(record) {
|
|
8350
|
+
const payload = record.payload;
|
|
8351
|
+
if (record.type === "queue.admitted") return payload;
|
|
8352
|
+
if (record.type === "queue.claimed") return { id: payload.id };
|
|
8353
|
+
if (record.type === "tool.started") return pick(payload, ["id", "tool", "inputHash", "retrySafe"]);
|
|
8354
|
+
if (record.type === "model.requested") return pick(payload, ["id", "agent", "model", "iteration", "attempt", "messagesHash"]);
|
|
8355
|
+
if (record.type === "turn.started") return pick(payload, ["id", "configHash", "promptHash"]);
|
|
8356
|
+
if (record.type === "savepoint.created") return pick(payload, ["id", "phase", "configHash"]);
|
|
8357
|
+
return payload;
|
|
8358
|
+
}
|
|
8359
|
+
function hasLegacyPayload(record) {
|
|
8360
|
+
if (record.type === "turn.started") return "config" in record.payload || "prompt" in record.payload;
|
|
8361
|
+
if (record.type === "model.requested") return "messages" in record.payload;
|
|
8362
|
+
if (record.type === "tool.started") return "input" in record.payload;
|
|
8363
|
+
if (record.type === "tool.completed") return "result" in record.payload;
|
|
8364
|
+
return false;
|
|
8365
|
+
}
|
|
8366
|
+
function pick(source, keys) {
|
|
8367
|
+
return Object.fromEntries(keys.flatMap((key) => key in source ? [[key, source[key]]] : []));
|
|
8368
|
+
}
|
|
8268
8369
|
function jsonSafeRecord(value) {
|
|
8269
8370
|
const json = JSON.stringify(value, (_key, item) => typeof item === "bigint" ? `${item}n` : item);
|
|
8270
8371
|
return JSON.parse(json);
|
|
@@ -20524,7 +20625,10 @@ async function createProductionRuntime(options) {
|
|
|
20524
20625
|
globalPluginDirs: [join24(home, ".flavor-code", "plugins")],
|
|
20525
20626
|
projectPluginDirs: [join24(workspace, ".flavor", "plugins")],
|
|
20526
20627
|
config,
|
|
20527
|
-
|
|
20628
|
+
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
20629
|
+
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
20630
|
+
// must stay opt-in until equivalent mediated capabilities are available.
|
|
20631
|
+
sandbox: options.pluginSandbox ?? false,
|
|
20528
20632
|
registrations: {
|
|
20529
20633
|
command(name, handler, description) {
|
|
20530
20634
|
if (typeof handler !== "function") throw new Error(`Plugin command "${name}" must be a function.`);
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module"; const require = createRequire(impor
|
|
|
3
3
|
import {
|
|
4
4
|
FlavorRpcServer,
|
|
5
5
|
TraceRecorder
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RDBTC35M.js";
|
|
7
7
|
import {
|
|
8
8
|
staticTaskLines
|
|
9
9
|
} from "./chunk-KKIBYDJI.js";
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
packageVersion,
|
|
24
24
|
redactErrorText,
|
|
25
25
|
runPalBroker
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-YKSMZDUZ.js";
|
|
27
27
|
import {
|
|
28
28
|
McpServerConfigSchema,
|
|
29
29
|
McpServerNameSchema,
|
|
@@ -877,7 +877,7 @@ async function runInteractiveCli(props) {
|
|
|
877
877
|
const [{ render, AlternateScreen }, { createElement }, { App }] = await Promise.all([
|
|
878
878
|
import("./claude-ink-ORUA7GWG.js"),
|
|
879
879
|
import("react"),
|
|
880
|
-
import("./app-
|
|
880
|
+
import("./app-EHB3XAQY.js")
|
|
881
881
|
]);
|
|
882
882
|
const instance = await render(createElement(
|
|
883
883
|
AlternateScreen,
|