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/dist/desktop/main.js
CHANGED
|
@@ -8025,7 +8025,7 @@ function toModelTool(tool) {
|
|
|
8025
8025
|
|
|
8026
8026
|
// src/harness/journal.ts
|
|
8027
8027
|
import { createHash as createHash6, randomUUID as randomUUID8 } from "crypto";
|
|
8028
|
-
import { closeSync, constants as constants2, existsSync as existsSync2, fsyncSync, mkdirSync, openSync, readFileSync, realpathSync as realpathSync2, statSync, truncateSync, writeSync } from "fs";
|
|
8028
|
+
import { closeSync, constants as constants2, existsSync as existsSync2, fsyncSync, mkdirSync, openSync, readFileSync, realpathSync as realpathSync2, renameSync, statSync, truncateSync, unlinkSync, writeSync } from "fs";
|
|
8029
8029
|
import { join as join8, relative as relative6, resolve as resolve8, sep as sep6 } from "path";
|
|
8030
8030
|
import { z as z15 } from "zod";
|
|
8031
8031
|
|
|
@@ -8036,8 +8036,10 @@ function harnessInvariantViolations(records) {
|
|
|
8036
8036
|
const tools = /* @__PURE__ */ new Set();
|
|
8037
8037
|
const models = /* @__PURE__ */ new Set();
|
|
8038
8038
|
const turns = /* @__PURE__ */ new Set();
|
|
8039
|
+
const firstSequence = records[0]?.sequence ?? 1;
|
|
8039
8040
|
records.forEach((record3, index) => {
|
|
8040
|
-
|
|
8041
|
+
const expectedSequence = firstSequence + index;
|
|
8042
|
+
if (record3.sequence !== expectedSequence) violations.push(`sequence ${record3.sequence} is not ${expectedSequence}`);
|
|
8041
8043
|
const id = typeof record3.payload.id === "string" ? record3.payload.id : "";
|
|
8042
8044
|
if (record3.type === "queue.admitted") {
|
|
8043
8045
|
if (queues.has(id)) violations.push(`queue ${id} was admitted twice`);
|
|
@@ -8053,13 +8055,13 @@ function harnessInvariantViolations(records) {
|
|
|
8053
8055
|
if (!tools.delete(id)) violations.push(`${record3.type} references unknown tool ${id}`);
|
|
8054
8056
|
} else if (record3.type === "model.requested") {
|
|
8055
8057
|
if (models.has(id)) violations.push(`model ${id} was requested twice`);
|
|
8056
|
-
if (!
|
|
8058
|
+
if (!isHash(record3.payload.messagesHash)) violations.push(`model ${id} has no valid messages hash`);
|
|
8057
8059
|
models.add(id);
|
|
8058
8060
|
} else if (record3.type === "model.completed") {
|
|
8059
8061
|
if (!models.delete(id)) violations.push(`model.completed references unknown model ${id}`);
|
|
8060
8062
|
} else if (record3.type === "turn.started") {
|
|
8061
8063
|
if (turns.has(id)) violations.push(`turn ${id} was started twice`);
|
|
8062
|
-
if (!(
|
|
8064
|
+
if (!isHash(record3.payload.promptHash)) violations.push(`turn ${id} has no valid prompt hash`);
|
|
8063
8065
|
turns.add(id);
|
|
8064
8066
|
} else if (record3.type === "turn.completed" || record3.type === "turn.interrupted") {
|
|
8065
8067
|
if (!turns.delete(id)) violations.push(`${record3.type} references unknown turn ${id}`);
|
|
@@ -8067,6 +8069,9 @@ function harnessInvariantViolations(records) {
|
|
|
8067
8069
|
});
|
|
8068
8070
|
return violations;
|
|
8069
8071
|
}
|
|
8072
|
+
function isHash(value) {
|
|
8073
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
8074
|
+
}
|
|
8070
8075
|
function assertHarnessInvariants(records) {
|
|
8071
8076
|
const violations = harnessInvariantViolations(records);
|
|
8072
8077
|
if (violations.length > 0) throw new Error(`Harness invariant violation: ${violations.join("; ")}`);
|
|
@@ -8147,7 +8152,7 @@ var HarnessJournal = class {
|
|
|
8147
8152
|
}
|
|
8148
8153
|
startTurn(config, prompt) {
|
|
8149
8154
|
const id = randomUUID8();
|
|
8150
|
-
this.#append("turn.started", { id,
|
|
8155
|
+
this.#append("turn.started", { id, configHash: hashJson(config), promptHash: hashJson(prompt) });
|
|
8151
8156
|
return id;
|
|
8152
8157
|
}
|
|
8153
8158
|
completeTurn(id) {
|
|
@@ -8158,7 +8163,14 @@ var HarnessJournal = class {
|
|
|
8158
8163
|
}
|
|
8159
8164
|
startModel(input) {
|
|
8160
8165
|
const id = randomUUID8();
|
|
8161
|
-
this.#append("model.requested", {
|
|
8166
|
+
this.#append("model.requested", {
|
|
8167
|
+
id,
|
|
8168
|
+
agent: input.agent,
|
|
8169
|
+
model: input.model,
|
|
8170
|
+
iteration: input.iteration,
|
|
8171
|
+
attempt: input.attempt,
|
|
8172
|
+
messagesHash: hashJson(input.messages)
|
|
8173
|
+
});
|
|
8162
8174
|
return id;
|
|
8163
8175
|
}
|
|
8164
8176
|
completeModel(id, completed, error) {
|
|
@@ -8166,11 +8178,11 @@ var HarnessJournal = class {
|
|
|
8166
8178
|
}
|
|
8167
8179
|
startTool(tool, input, retrySafe) {
|
|
8168
8180
|
const id = randomUUID8();
|
|
8169
|
-
this.#append("tool.started", { id, tool,
|
|
8181
|
+
this.#append("tool.started", { id, tool, inputHash: hashJson(input), retrySafe });
|
|
8170
8182
|
return id;
|
|
8171
8183
|
}
|
|
8172
8184
|
completeTool(id, result) {
|
|
8173
|
-
this.#append("tool.completed", { id,
|
|
8185
|
+
this.#append("tool.completed", { id, resultHash: hashJson(result) });
|
|
8174
8186
|
}
|
|
8175
8187
|
interruptTool(id, reason) {
|
|
8176
8188
|
this.#append("tool.interrupted", { id, reason });
|
|
@@ -8247,47 +8259,153 @@ var HarnessJournal = class {
|
|
|
8247
8259
|
const committed = raw.endsWith("\n") ? raw : raw.slice(0, Math.max(0, raw.lastIndexOf("\n") + 1));
|
|
8248
8260
|
if (committed.length !== raw.length) truncateSync(this.#path, Buffer.byteLength(committed));
|
|
8249
8261
|
const lines = committed.split("\n").filter(Boolean);
|
|
8250
|
-
let previousHash
|
|
8262
|
+
let previousHash;
|
|
8263
|
+
let expectedSequence;
|
|
8251
8264
|
for (const line of lines) {
|
|
8252
8265
|
const record3 = JournalRecordSchema.parse(JSON.parse(line));
|
|
8253
|
-
|
|
8266
|
+
expectedSequence ??= record3.sequence;
|
|
8267
|
+
previousHash ??= record3.previousHash;
|
|
8268
|
+
if (record3.sequence !== expectedSequence) throw new Error("Harness journal sequence is not contiguous");
|
|
8254
8269
|
if (record3.previousHash !== previousHash) throw new Error("Harness journal hash chain is broken");
|
|
8255
8270
|
if (record3.hash !== recordHash(record3)) throw new Error("Harness journal record hash is invalid");
|
|
8256
8271
|
this.#records.push(record3);
|
|
8257
8272
|
previousHash = record3.hash;
|
|
8273
|
+
expectedSequence += 1;
|
|
8258
8274
|
}
|
|
8259
|
-
this.#sequence = this.#records.
|
|
8260
|
-
this.#lastHash = previousHash;
|
|
8275
|
+
this.#sequence = this.#records.at(-1)?.sequence ?? 0;
|
|
8276
|
+
this.#lastHash = previousHash ?? null;
|
|
8261
8277
|
assertHarnessInvariants(this.#records);
|
|
8278
|
+
const hasRotatedPrefix = (this.#records[0]?.sequence ?? 1) !== 1;
|
|
8279
|
+
if (metadata.size > this.#maxBytes * 0.75 || hasRotatedPrefix || this.#records.some(hasLegacyPayload)) this.#compact();
|
|
8262
8280
|
}
|
|
8263
8281
|
#append(type, payload) {
|
|
8264
8282
|
const normalizedPayload = jsonSafeRecord(payload);
|
|
8283
|
+
let record3 = this.#createRecord(type, normalizedPayload);
|
|
8284
|
+
let line = `${JSON.stringify(record3)}
|
|
8285
|
+
`;
|
|
8286
|
+
let priorBytes = existsSync2(this.#path) ? statSync(this.#path).size : 0;
|
|
8287
|
+
if (priorBytes + Buffer.byteLength(line) > this.#maxBytes) {
|
|
8288
|
+
this.#compact();
|
|
8289
|
+
record3 = this.#createRecord(type, normalizedPayload);
|
|
8290
|
+
line = `${JSON.stringify(record3)}
|
|
8291
|
+
`;
|
|
8292
|
+
priorBytes = existsSync2(this.#path) ? statSync(this.#path).size : 0;
|
|
8293
|
+
if (priorBytes + Buffer.byteLength(line) > this.#maxBytes) {
|
|
8294
|
+
throw new Error(`Harness journal event exceeds the ${this.#maxBytes} byte bounded journal capacity`);
|
|
8295
|
+
}
|
|
8296
|
+
}
|
|
8297
|
+
const fd = openSync(this.#path, constants2.O_CREAT | constants2.O_APPEND | constants2.O_WRONLY, 384);
|
|
8298
|
+
try {
|
|
8299
|
+
writeSync(fd, line, void 0, "utf8");
|
|
8300
|
+
fsyncSync(fd);
|
|
8301
|
+
} finally {
|
|
8302
|
+
closeSync(fd);
|
|
8303
|
+
}
|
|
8304
|
+
this.#records.push(record3);
|
|
8305
|
+
this.#sequence = record3.sequence;
|
|
8306
|
+
this.#lastHash = record3.hash;
|
|
8307
|
+
}
|
|
8308
|
+
#createRecord(type, payload) {
|
|
8265
8309
|
const unsigned = {
|
|
8266
8310
|
version: HARNESS_JOURNAL_VERSION,
|
|
8267
8311
|
sequence: this.#sequence + 1,
|
|
8268
8312
|
id: randomUUID8(),
|
|
8269
8313
|
timestamp: this.#now(),
|
|
8270
8314
|
type,
|
|
8271
|
-
payload
|
|
8315
|
+
payload,
|
|
8272
8316
|
previousHash: this.#lastHash
|
|
8273
8317
|
};
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
const
|
|
8278
|
-
|
|
8279
|
-
const
|
|
8318
|
+
return JournalRecordSchema.parse({ ...unsigned, hash: hashJson(unsigned) });
|
|
8319
|
+
}
|
|
8320
|
+
#compact() {
|
|
8321
|
+
const queues = /* @__PURE__ */ new Map();
|
|
8322
|
+
const claims = /* @__PURE__ */ new Map();
|
|
8323
|
+
const tools = /* @__PURE__ */ new Map();
|
|
8324
|
+
const models = /* @__PURE__ */ new Map();
|
|
8325
|
+
const turns = /* @__PURE__ */ new Map();
|
|
8326
|
+
let savepoint;
|
|
8327
|
+
for (const record3 of this.#records) {
|
|
8328
|
+
const id = typeof record3.payload.id === "string" ? record3.payload.id : "";
|
|
8329
|
+
if (record3.type === "queue.admitted") queues.set(id, record3);
|
|
8330
|
+
else if (record3.type === "queue.claimed") claims.set(id, record3);
|
|
8331
|
+
else if (record3.type === "queue.released") claims.delete(id);
|
|
8332
|
+
else if (record3.type === "queue.acked") {
|
|
8333
|
+
queues.delete(id);
|
|
8334
|
+
claims.delete(id);
|
|
8335
|
+
} else if (record3.type === "tool.started") tools.set(id, record3);
|
|
8336
|
+
else if (record3.type === "tool.completed" || record3.type === "tool.interrupted") tools.delete(id);
|
|
8337
|
+
else if (record3.type === "model.requested") models.set(id, record3);
|
|
8338
|
+
else if (record3.type === "model.completed") models.delete(id);
|
|
8339
|
+
else if (record3.type === "turn.started") turns.set(id, record3);
|
|
8340
|
+
else if (record3.type === "turn.completed" || record3.type === "turn.interrupted") turns.delete(id);
|
|
8341
|
+
else if (record3.type === "savepoint.created") savepoint = record3;
|
|
8342
|
+
}
|
|
8343
|
+
const retained = [
|
|
8344
|
+
...queues.values(),
|
|
8345
|
+
...claims.values(),
|
|
8346
|
+
...tools.values(),
|
|
8347
|
+
...models.values(),
|
|
8348
|
+
...turns.values(),
|
|
8349
|
+
...savepoint === void 0 ? [] : [savepoint]
|
|
8350
|
+
].sort((left, right) => left.sequence - right.sequence);
|
|
8351
|
+
const compacted = [];
|
|
8352
|
+
let previousHash = null;
|
|
8353
|
+
for (const source of retained) {
|
|
8354
|
+
const unsigned = {
|
|
8355
|
+
version: HARNESS_JOURNAL_VERSION,
|
|
8356
|
+
sequence: compacted.length + 1,
|
|
8357
|
+
id: randomUUID8(),
|
|
8358
|
+
timestamp: source.timestamp,
|
|
8359
|
+
type: source.type,
|
|
8360
|
+
payload: compactPayload(source),
|
|
8361
|
+
previousHash
|
|
8362
|
+
};
|
|
8363
|
+
const record3 = JournalRecordSchema.parse({ ...unsigned, hash: hashJson(unsigned) });
|
|
8364
|
+
compacted.push(record3);
|
|
8365
|
+
previousHash = record3.hash;
|
|
8366
|
+
}
|
|
8367
|
+
const contents = compacted.map((record3) => `${JSON.stringify(record3)}
|
|
8368
|
+
`).join("");
|
|
8369
|
+
if (Buffer.byteLength(contents) > this.#maxBytes) return;
|
|
8370
|
+
const temporary = `${this.#path}.compact-${process.pid}-${randomUUID8()}`;
|
|
8371
|
+
const fd = openSync(temporary, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY, 384);
|
|
8280
8372
|
try {
|
|
8281
|
-
writeSync(fd,
|
|
8373
|
+
writeSync(fd, contents, void 0, "utf8");
|
|
8282
8374
|
fsyncSync(fd);
|
|
8283
8375
|
} finally {
|
|
8284
8376
|
closeSync(fd);
|
|
8285
8377
|
}
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8378
|
+
try {
|
|
8379
|
+
renameSync(temporary, this.#path);
|
|
8380
|
+
} catch (error) {
|
|
8381
|
+
if (existsSync2(temporary)) unlinkSync(temporary);
|
|
8382
|
+
throw error;
|
|
8383
|
+
}
|
|
8384
|
+
this.#records = compacted;
|
|
8385
|
+
this.#sequence = compacted.length;
|
|
8386
|
+
this.#lastHash = previousHash;
|
|
8289
8387
|
}
|
|
8290
8388
|
};
|
|
8389
|
+
function compactPayload(record3) {
|
|
8390
|
+
const payload = record3.payload;
|
|
8391
|
+
if (record3.type === "queue.admitted") return payload;
|
|
8392
|
+
if (record3.type === "queue.claimed") return { id: payload.id };
|
|
8393
|
+
if (record3.type === "tool.started") return pick(payload, ["id", "tool", "inputHash", "retrySafe"]);
|
|
8394
|
+
if (record3.type === "model.requested") return pick(payload, ["id", "agent", "model", "iteration", "attempt", "messagesHash"]);
|
|
8395
|
+
if (record3.type === "turn.started") return pick(payload, ["id", "configHash", "promptHash"]);
|
|
8396
|
+
if (record3.type === "savepoint.created") return pick(payload, ["id", "phase", "configHash"]);
|
|
8397
|
+
return payload;
|
|
8398
|
+
}
|
|
8399
|
+
function hasLegacyPayload(record3) {
|
|
8400
|
+
if (record3.type === "turn.started") return "config" in record3.payload || "prompt" in record3.payload;
|
|
8401
|
+
if (record3.type === "model.requested") return "messages" in record3.payload;
|
|
8402
|
+
if (record3.type === "tool.started") return "input" in record3.payload;
|
|
8403
|
+
if (record3.type === "tool.completed") return "result" in record3.payload;
|
|
8404
|
+
return false;
|
|
8405
|
+
}
|
|
8406
|
+
function pick(source, keys) {
|
|
8407
|
+
return Object.fromEntries(keys.flatMap((key) => key in source ? [[key, source[key]]] : []));
|
|
8408
|
+
}
|
|
8291
8409
|
function jsonSafeRecord(value) {
|
|
8292
8410
|
const json = JSON.stringify(value, (_key, item) => typeof item === "bigint" ? `${item}n` : item);
|
|
8293
8411
|
return JSON.parse(json);
|
|
@@ -10294,7 +10412,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
10294
10412
|
|
|
10295
10413
|
// src/init/project.ts
|
|
10296
10414
|
import { lstat as lstat4, mkdir as mkdir9, readdir as readdir4, readFile as readFile9, realpath as realpath7, writeFile as writeFile8 } from "fs/promises";
|
|
10297
|
-
import { copyFileSync, mkdirSync as mkdirSync2, readdirSync
|
|
10415
|
+
import { copyFileSync, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
10298
10416
|
import { basename as basename4, dirname as dirname9, isAbsolute as isAbsolute7, join as join12, relative as relative8, sep as sep8 } from "path";
|
|
10299
10417
|
import { fileURLToPath } from "url";
|
|
10300
10418
|
var GENERATED_START = "<!-- flavor-code:start -->";
|
|
@@ -10747,22 +10865,6 @@ async function createExampleFlavorConfig(cwd) {
|
|
|
10747
10865
|
};
|
|
10748
10866
|
await writeFile8(configPath, JSON.stringify(example, null, 2) + "\n");
|
|
10749
10867
|
}
|
|
10750
|
-
var CODEISLAND_SRC_DIR = join12(dirname9(fileURLToPath(import.meta.url)), "codeisland");
|
|
10751
|
-
var CODEISLAND_FILES = ["flavor-plugin.json", "activate.mjs", "bridge.mjs"];
|
|
10752
|
-
async function copyCodeIslandPlugin(cwd) {
|
|
10753
|
-
const pluginDir = join12(cwd, ".flavor", "plugins", "codeisland");
|
|
10754
|
-
try {
|
|
10755
|
-
await lstat4(pluginDir);
|
|
10756
|
-
return;
|
|
10757
|
-
} catch (error) {
|
|
10758
|
-
if (!isMissingPathError(error)) throw error;
|
|
10759
|
-
}
|
|
10760
|
-
await mkdir9(pluginDir, { recursive: true, mode: 448 });
|
|
10761
|
-
for (const name of CODEISLAND_FILES) {
|
|
10762
|
-
const content = readFileSync2(join12(CODEISLAND_SRC_DIR, name), "utf8");
|
|
10763
|
-
await writeFile8(join12(pluginDir, name), content);
|
|
10764
|
-
}
|
|
10765
|
-
}
|
|
10766
10868
|
var ASTGRAPH_SRC_DIR = join12(dirname9(fileURLToPath(import.meta.url)), "astgraph");
|
|
10767
10869
|
async function copyAstgraphPlugin(cwd) {
|
|
10768
10870
|
const pluginDir = join12(cwd, ".flavor", "plugins", "astgraph");
|
|
@@ -10785,7 +10887,6 @@ function copyDirectory(from, to) {
|
|
|
10785
10887
|
}
|
|
10786
10888
|
async function initializeFlavor(cwd) {
|
|
10787
10889
|
await ensureFlavorDirectories(cwd);
|
|
10788
|
-
await copyCodeIslandPlugin(cwd);
|
|
10789
10890
|
await copyAstgraphPlugin(cwd);
|
|
10790
10891
|
await createExampleFlavorConfig(cwd);
|
|
10791
10892
|
const facts = await inspectProject(cwd);
|
|
@@ -13686,7 +13787,7 @@ import {
|
|
|
13686
13787
|
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
13687
13788
|
|
|
13688
13789
|
// src/utils/version.ts
|
|
13689
|
-
import { readFileSync as
|
|
13790
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
13690
13791
|
import { dirname as dirname13, join as join20 } from "path";
|
|
13691
13792
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13692
13793
|
var cached;
|
|
@@ -13695,7 +13796,7 @@ function packageVersion() {
|
|
|
13695
13796
|
let directory = dirname13(fileURLToPath2(import.meta.url));
|
|
13696
13797
|
for (; ; ) {
|
|
13697
13798
|
try {
|
|
13698
|
-
const manifest = JSON.parse(
|
|
13799
|
+
const manifest = JSON.parse(readFileSync2(join20(directory, "package.json"), "utf8"));
|
|
13699
13800
|
if (typeof manifest.version === "string" && manifest.version.length > 0) {
|
|
13700
13801
|
cached = manifest.version;
|
|
13701
13802
|
return cached;
|
|
@@ -24309,7 +24410,10 @@ async function createProductionRuntime(options) {
|
|
|
24309
24410
|
globalPluginDirs: [join31(home, ".flavor-code", "plugins")],
|
|
24310
24411
|
projectPluginDirs: [join31(workspace, ".flavor", "plugins")],
|
|
24311
24412
|
config,
|
|
24312
|
-
|
|
24413
|
+
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
24414
|
+
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
24415
|
+
// must stay opt-in until equivalent mediated capabilities are available.
|
|
24416
|
+
sandbox: options.pluginSandbox ?? false,
|
|
24313
24417
|
registrations: {
|
|
24314
24418
|
command(name, handler, description) {
|
|
24315
24419
|
if (typeof handler !== "function") throw new Error(`Plugin command "${name}" must be a function.`);
|