flavor-code 1.2.14 → 1.2.15
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 +8 -3
- package/README.zh-CN.md +8 -3
- package/dist/agent/planner.d.ts +2 -0
- package/dist/{app-UJAGMKD5.js → app-3KW2FTVJ.js} +1 -1
- package/dist/{chunk-4YXDGGIC.js → chunk-5TG3E2EJ.js} +561 -74
- package/dist/{chunk-KL6TR2KG.js → chunk-BKARQJQZ.js} +1 -1
- package/dist/cli.js +78 -16
- package/dist/desktop/main.js +875 -328
- package/dist/desktop-renderer/assets/{index-CcD0D6x0.js → index-BCJjMvbX.js} +1 -1
- package/dist/desktop-renderer/index.html +1 -1
- package/dist/evolve/store.d.ts +26 -0
- package/dist/git/insights.d.ts +52 -0
- package/dist/git/service.d.ts +43 -0
- package/dist/git/tools.d.ts +9 -0
- package/dist/sdk/index.js +2 -2
- package/dist/session/store.d.ts +1 -0
- package/dist/ui/commands.d.ts +8 -2
- package/dist/ui/session.d.ts +2 -0
- package/package.json +1 -1
|
@@ -2916,6 +2916,8 @@ var MVP_COMMANDS = [
|
|
|
2916
2916
|
"goal",
|
|
2917
2917
|
"evolve",
|
|
2918
2918
|
"mcp",
|
|
2919
|
+
"commit",
|
|
2920
|
+
"review",
|
|
2919
2921
|
"ide",
|
|
2920
2922
|
"memory",
|
|
2921
2923
|
"remember",
|
|
@@ -2950,6 +2952,8 @@ var COMMAND_DESCRIPTIONS = {
|
|
|
2950
2952
|
loop: "Run a verified autonomous loop toward a goal",
|
|
2951
2953
|
goal: "Run a goal pipeline with adversarial verification",
|
|
2952
2954
|
evolve: "Self-improvement loop: capture failures, suggest fixes, verify with tests",
|
|
2955
|
+
commit: "Generate a commit message for staged changes and commit after confirmation",
|
|
2956
|
+
review: "Review uncommitted changes for bugs and risks before committing",
|
|
2953
2957
|
mcp: "Manage MCP servers",
|
|
2954
2958
|
ide: "Show the connected IDE and editor context",
|
|
2955
2959
|
memory: "Show long-term project memory",
|
|
@@ -3001,6 +3005,14 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
|
|
|
3001
3005
|
if (name === "evolve") {
|
|
3002
3006
|
return { name, args };
|
|
3003
3007
|
}
|
|
3008
|
+
if (name === "commit") {
|
|
3009
|
+
const hint = args.join(" ").trim();
|
|
3010
|
+
return hint.length === 0 ? { name } : { name, hint };
|
|
3011
|
+
}
|
|
3012
|
+
if (name === "review") {
|
|
3013
|
+
const focus = args.join(" ").trim();
|
|
3014
|
+
return focus.length === 0 ? { name } : { name, focus };
|
|
3015
|
+
}
|
|
3004
3016
|
if (name === "loop") {
|
|
3005
3017
|
const goal = args.join(" ").trim();
|
|
3006
3018
|
if (!goal) return { name: "invalid", command: name, message: "Use /loop <goal>." };
|
|
@@ -3208,6 +3220,8 @@ var HELP = [
|
|
|
3208
3220
|
"/compact /clear /help /exit",
|
|
3209
3221
|
"/loop <goal> run a verified autonomous loop",
|
|
3210
3222
|
"/goal <objective> run a goal pipeline with adversarial verification",
|
|
3223
|
+
"/commit [hint] draft a commit message for staged changes and commit",
|
|
3224
|
+
"/review [focus] review uncommitted changes before committing",
|
|
3211
3225
|
"/evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>> self-improvement loop",
|
|
3212
3226
|
"/mcp [status|tools|reconnect|enable|disable] manage MCP servers",
|
|
3213
3227
|
"/ide show VS Code connection and cursor/selection",
|
|
@@ -3548,6 +3562,10 @@ var FlavorSession = class {
|
|
|
3548
3562
|
for await (const event of this.#services.runLoop(command.goal, signal)) this.#services.output(event);
|
|
3549
3563
|
} else if (command.name === "goal") {
|
|
3550
3564
|
for await (const event of this.#services.runGoal(command.goal, signal)) this.#services.output(event);
|
|
3565
|
+
} else if (command.name === "commit") {
|
|
3566
|
+
this.#notice(await required(this.#services.gitCommit, "commit")(command.hint, signal));
|
|
3567
|
+
} else if (command.name === "review") {
|
|
3568
|
+
this.#notice(await required(this.#services.gitReview, "review")(command.focus, signal));
|
|
3551
3569
|
} else if (command.name === "mcp") {
|
|
3552
3570
|
this.#notice(await this.#services.mcp(command, signal));
|
|
3553
3571
|
} else if (command.name === "pals") {
|
|
@@ -4919,7 +4937,10 @@ var TaskNodeSchema = z4.object({
|
|
|
4919
4937
|
description: z4.string().trim().min(1),
|
|
4920
4938
|
dependencies: z4.array(z4.string().trim().min(1)),
|
|
4921
4939
|
expectedOutputs: z4.array(z4.string().trim().min(1)),
|
|
4922
|
-
verification: z4.array(z4.string().trim().min(1))
|
|
4940
|
+
verification: z4.array(z4.string().trim().min(1)),
|
|
4941
|
+
files: z4.array(z4.string().trim().min(1)).describe(
|
|
4942
|
+
"Workspace files this task owns for writing. Tasks whose files overlap are never run in parallel, so declare every file this task may create or modify."
|
|
4943
|
+
).optional()
|
|
4923
4944
|
}).strict();
|
|
4924
4945
|
var TaskGraphSchema = z4.object({
|
|
4925
4946
|
nodes: z4.array(TaskNodeSchema)
|
|
@@ -5106,6 +5127,7 @@ var SubagentScheduler = class {
|
|
|
5106
5127
|
const states = new Map(graph.nodes.map((task) => [task.id, "pending"]));
|
|
5107
5128
|
const results = /* @__PURE__ */ new Map();
|
|
5108
5129
|
const running = /* @__PURE__ */ new Map();
|
|
5130
|
+
const runningFiles = /* @__PURE__ */ new Map();
|
|
5109
5131
|
const started = /* @__PURE__ */ new Set();
|
|
5110
5132
|
try {
|
|
5111
5133
|
while (statesHaveWork(states)) {
|
|
@@ -5114,7 +5136,9 @@ var SubagentScheduler = class {
|
|
|
5114
5136
|
for (const task of graph.nodes) {
|
|
5115
5137
|
if (running.size >= this.#options.maxSubagents) break;
|
|
5116
5138
|
if (states.get(task.id) !== "pending" || !isReady(task, states)) continue;
|
|
5139
|
+
if (conflictsWithRunning(task, runningFiles)) continue;
|
|
5117
5140
|
states.set(task.id, "running");
|
|
5141
|
+
if (task.files !== void 0) runningFiles.set(task.id, task.files.map(normalizeOwnedFile));
|
|
5118
5142
|
const execution = this.#runTask(task, signal);
|
|
5119
5143
|
running.set(task.id, execution);
|
|
5120
5144
|
started.add(execution);
|
|
@@ -5127,6 +5151,7 @@ var SubagentScheduler = class {
|
|
|
5127
5151
|
const completion = await Promise.race(running.values());
|
|
5128
5152
|
signal.throwIfAborted();
|
|
5129
5153
|
running.delete(completion.id);
|
|
5154
|
+
runningFiles.delete(completion.id);
|
|
5130
5155
|
results.set(completion.id, completion.result);
|
|
5131
5156
|
states.set(completion.id, completion.result.status);
|
|
5132
5157
|
await this.#options.onResult?.(completion.result);
|
|
@@ -5204,6 +5229,26 @@ var SubagentScheduler = class {
|
|
|
5204
5229
|
function isReady(task, states) {
|
|
5205
5230
|
return task.dependencies.every((dependency) => states.get(dependency) === "completed");
|
|
5206
5231
|
}
|
|
5232
|
+
function normalizeOwnedFile(value) {
|
|
5233
|
+
return value.replace(/\\/g, "/");
|
|
5234
|
+
}
|
|
5235
|
+
function ownedFilesConflict(left, right) {
|
|
5236
|
+
for (const one of left) {
|
|
5237
|
+
for (const other of right) {
|
|
5238
|
+
if (one === other) return true;
|
|
5239
|
+
if (other.startsWith(`${one}/`) || one.startsWith(`${other}/`)) return true;
|
|
5240
|
+
}
|
|
5241
|
+
}
|
|
5242
|
+
return false;
|
|
5243
|
+
}
|
|
5244
|
+
function conflictsWithRunning(task, runningFiles) {
|
|
5245
|
+
if (task.files === void 0 || task.files.length === 0) return false;
|
|
5246
|
+
const owned = task.files.map(normalizeOwnedFile);
|
|
5247
|
+
for (const files of runningFiles.values()) {
|
|
5248
|
+
if (ownedFilesConflict(owned, files)) return true;
|
|
5249
|
+
}
|
|
5250
|
+
return false;
|
|
5251
|
+
}
|
|
5207
5252
|
function statesHaveWork(states) {
|
|
5208
5253
|
return [...states.values()].some((state) => state === "pending" || state === "running");
|
|
5209
5254
|
}
|
|
@@ -8618,23 +8663,45 @@ async function readJsonArray(file) {
|
|
|
8618
8663
|
return [];
|
|
8619
8664
|
}
|
|
8620
8665
|
}
|
|
8666
|
+
async function readRules(file) {
|
|
8667
|
+
try {
|
|
8668
|
+
const parsed = JSON.parse(await readFile7(file, "utf8"));
|
|
8669
|
+
if (!Array.isArray(parsed)) return [];
|
|
8670
|
+
return parsed.filter((item) => item !== null && typeof item === "object").filter((item) => typeof item.id === "string" && typeof item.text === "string").map((item) => ({
|
|
8671
|
+
id: item.id,
|
|
8672
|
+
text: item.text,
|
|
8673
|
+
addedAt: typeof item.addedAt === "string" ? item.addedAt : "",
|
|
8674
|
+
...typeof item.sourceId === "string" ? { sourceId: item.sourceId } : {}
|
|
8675
|
+
}));
|
|
8676
|
+
} catch {
|
|
8677
|
+
return [];
|
|
8678
|
+
}
|
|
8679
|
+
}
|
|
8680
|
+
async function writeRules(file, rules) {
|
|
8681
|
+
await mkdir9(dirname10(file), { recursive: true, mode: 448 });
|
|
8682
|
+
await writeFile7(file, JSON.stringify(rules, null, 2), { encoding: "utf8", mode: 384 });
|
|
8683
|
+
}
|
|
8621
8684
|
var EvolveStore = class {
|
|
8622
8685
|
dir;
|
|
8623
8686
|
signalsFile;
|
|
8624
8687
|
reflectionsFile;
|
|
8625
8688
|
doneFile;
|
|
8626
8689
|
verifiedFile;
|
|
8690
|
+
rulesFile;
|
|
8627
8691
|
maxSignals;
|
|
8692
|
+
maxRules;
|
|
8628
8693
|
// Serialize file mutations: tool hooks may fire in bursts.
|
|
8629
8694
|
#queue = Promise.resolve();
|
|
8630
8695
|
constructor(options) {
|
|
8631
|
-
const { workspace, maxSignals = 400 } = options;
|
|
8696
|
+
const { workspace, maxSignals = 400, maxRules = 20 } = options;
|
|
8632
8697
|
this.dir = join9(workspace, ".flavor", "evolve");
|
|
8633
8698
|
this.signalsFile = join9(this.dir, "signals.jsonl");
|
|
8634
8699
|
this.reflectionsFile = join9(this.dir, "reflections.jsonl");
|
|
8635
8700
|
this.doneFile = join9(this.dir, "done.json");
|
|
8636
8701
|
this.verifiedFile = join9(this.dir, "verified.json");
|
|
8702
|
+
this.rulesFile = join9(this.dir, "rules.json");
|
|
8637
8703
|
this.maxSignals = maxSignals;
|
|
8704
|
+
this.maxRules = maxRules;
|
|
8638
8705
|
}
|
|
8639
8706
|
#enqueue(operation) {
|
|
8640
8707
|
const next = this.#queue.then(operation, operation);
|
|
@@ -8780,6 +8847,43 @@ var EvolveStore = class {
|
|
|
8780
8847
|
}
|
|
8781
8848
|
});
|
|
8782
8849
|
}
|
|
8850
|
+
/** Learned guardrail rules, oldest first. */
|
|
8851
|
+
listRules() {
|
|
8852
|
+
return this.#enqueue(() => readRules(this.rulesFile));
|
|
8853
|
+
}
|
|
8854
|
+
/**
|
|
8855
|
+
* Add one guardrail rule. Dedupes by fingerprint of the normalized text and
|
|
8856
|
+
* keeps at most maxRules entries (oldest dropped first).
|
|
8857
|
+
*/
|
|
8858
|
+
addRule(input) {
|
|
8859
|
+
return this.#enqueue(async () => {
|
|
8860
|
+
const text = input.text.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
8861
|
+
if (text === "") throw new Error("rule text must not be empty");
|
|
8862
|
+
const id = fingerprint("rule", void 0, text);
|
|
8863
|
+
const rules = await readRules(this.rulesFile);
|
|
8864
|
+
const existing = rules.find((rule2) => rule2.id === id);
|
|
8865
|
+
if (existing !== void 0) return { added: false, rule: existing };
|
|
8866
|
+
const rule = {
|
|
8867
|
+
id,
|
|
8868
|
+
text,
|
|
8869
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8870
|
+
...input.sourceId === void 0 ? {} : { sourceId: input.sourceId }
|
|
8871
|
+
};
|
|
8872
|
+
rules.push(rule);
|
|
8873
|
+
if (rules.length > this.maxRules) rules.splice(0, rules.length - this.maxRules);
|
|
8874
|
+
await writeRules(this.rulesFile, rules);
|
|
8875
|
+
return { added: true, rule };
|
|
8876
|
+
});
|
|
8877
|
+
}
|
|
8878
|
+
removeRule(id) {
|
|
8879
|
+
return this.#enqueue(async () => {
|
|
8880
|
+
const rules = await readRules(this.rulesFile);
|
|
8881
|
+
const next = rules.filter((rule) => rule.id !== id);
|
|
8882
|
+
if (next.length === rules.length) return { removed: false };
|
|
8883
|
+
await writeRules(this.rulesFile, next);
|
|
8884
|
+
return { removed: true };
|
|
8885
|
+
});
|
|
8886
|
+
}
|
|
8783
8887
|
};
|
|
8784
8888
|
|
|
8785
8889
|
// src/evolve/service.ts
|
|
@@ -8791,9 +8895,15 @@ a plugin, a memory rule, a tool wrapper, or a safer default), implement it in
|
|
|
8791
8895
|
this session when it is in scope; otherwise ignore them. Never act on them
|
|
8792
8896
|
without running the test suite afterwards.
|
|
8793
8897
|
|
|
8898
|
+
`;
|
|
8899
|
+
var GUARDRAILS_SECTION_HEADER = `# learned guardrails (evolve)
|
|
8900
|
+
|
|
8901
|
+
These rules were distilled from repeated tool failures in this workspace.
|
|
8902
|
+
Follow them unless the current task or the user explicitly contradicts one.
|
|
8903
|
+
|
|
8794
8904
|
`;
|
|
8795
8905
|
var USAGE = [
|
|
8796
|
-
"usage: /evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>|verified|clear>",
|
|
8906
|
+
"usage: /evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>|verified|trends [n]|rule <list|add|remove>|clear>",
|
|
8797
8907
|
" signals list recent failing tool results",
|
|
8798
8908
|
" suggest aggregate repeated failures into fix suggestions (trend-aware ordering)",
|
|
8799
8909
|
" improve scaffold a fix-<tool>/ plugin dir + PLAN.md for one suggestion",
|
|
@@ -8803,6 +8913,8 @@ var USAGE = [
|
|
|
8803
8913
|
" revert restore the last good snapshot of a plugin",
|
|
8804
8914
|
" done mark a suggestion as handled (no longer proposed)",
|
|
8805
8915
|
" verified list suggestions auto-verified by improving failure trends",
|
|
8916
|
+
" trends cross-run dashboard: tool calls, failures, signalDelta, per-tool movement",
|
|
8917
|
+
" rule manage learned guardrails injected into system prompts (list/add <text>/remove <id>)",
|
|
8806
8918
|
" clear reset signals, done markers, and verified markers"
|
|
8807
8919
|
].join("\n");
|
|
8808
8920
|
function runCommand(command, cwd, timeoutMs) {
|
|
@@ -8894,9 +9006,17 @@ function createEvolveService(options) {
|
|
|
8894
9006
|
}
|
|
8895
9007
|
async function refreshPromptCache() {
|
|
8896
9008
|
try {
|
|
8897
|
-
const suggestions = await openWithTrends(promptTop);
|
|
8898
|
-
|
|
8899
|
-
|
|
9009
|
+
const [suggestions, rules] = await Promise.all([openWithTrends(promptTop), store.listRules()]);
|
|
9010
|
+
const sections = [];
|
|
9011
|
+
if (rules.length > 0) {
|
|
9012
|
+
sections.push(`${GUARDRAILS_SECTION_HEADER}${rules.map((rule) => `- ${rule.text}`).join("\n")}
|
|
9013
|
+
`);
|
|
9014
|
+
}
|
|
9015
|
+
if (suggestions.length > 0) {
|
|
9016
|
+
sections.push(`${SUGGEST_SECTION_HEADER}${suggestions.map((suggestion) => `- [${suggestion.id}] ${suggestion.hint}`).join("\n")}
|
|
9017
|
+
`);
|
|
9018
|
+
}
|
|
9019
|
+
promptCache = sections.length === 0 ? void 0 : sections.join("\n");
|
|
8900
9020
|
} catch (error) {
|
|
8901
9021
|
logger.warn(`prompt section failed \u2014 ${error instanceof Error ? error.message : String(error)}`);
|
|
8902
9022
|
}
|
|
@@ -9076,6 +9196,41 @@ function createEvolveService(options) {
|
|
|
9076
9196
|
return signal === void 0 ? `[${id}] (signal no longer recorded)` : `[${id}] ${signal.tool} x${signal.count} \u2014 ${signal.error} (verified)`;
|
|
9077
9197
|
}).join("\n");
|
|
9078
9198
|
}
|
|
9199
|
+
if (arg === "trends" || arg.startsWith("trends ")) {
|
|
9200
|
+
const requested = Number.parseInt(arg.slice(6).trim(), 10);
|
|
9201
|
+
const count = Number.isNaN(requested) ? 5 : Math.min(Math.max(requested, 1), 50);
|
|
9202
|
+
const reflections = await store.reflections(count);
|
|
9203
|
+
if (reflections.length === 0) return "no reflections recorded yet \u2014 trends appear after runs end";
|
|
9204
|
+
const sign = (value) => value > 0 ? `+${value}` : String(value);
|
|
9205
|
+
const lines = [`evolve trends (last ${reflections.length} run(s), newest first)`];
|
|
9206
|
+
for (const reflection of reflections) {
|
|
9207
|
+
lines.push(`${reflection.at.replace("T", " ").slice(0, 19)} ${reflection.reason}: model calls ${reflection.iterations}, tool calls ${reflection.toolCalls} (${reflection.toolErrors} failed), failures ${reflection.totalFailures} (delta ${sign(reflection.signalDelta)})`);
|
|
9208
|
+
const moved = Object.entries(reflection.perTool ?? {}).filter(([, trend]) => trend.delta !== 0);
|
|
9209
|
+
for (const [tool, trend] of moved) {
|
|
9210
|
+
lines.push(` - ${tool}: ${trend.failures} failure(s) this run (${sign(trend.delta)} vs previous)`);
|
|
9211
|
+
}
|
|
9212
|
+
}
|
|
9213
|
+
return lines.join("\n");
|
|
9214
|
+
}
|
|
9215
|
+
if (arg === "rule" || arg === "rule list") {
|
|
9216
|
+
const rules = await store.listRules();
|
|
9217
|
+
if (rules.length === 0) return "no guardrail rules yet \u2014 add one with /evolve rule add <text>";
|
|
9218
|
+
return rules.map((rule) => `[${rule.id}] ${rule.text}`).join("\n");
|
|
9219
|
+
}
|
|
9220
|
+
if (arg.startsWith("rule add ")) {
|
|
9221
|
+
const text = arg.slice(9).trim();
|
|
9222
|
+
if (text === "") return "usage: /evolve rule add <text>";
|
|
9223
|
+
const { added, rule } = await store.addRule({ text });
|
|
9224
|
+
await refreshPromptCache();
|
|
9225
|
+
return added ? `added guardrail [${rule.id}]: ${rule.text}` : `guardrail already exists [${rule.id}]: ${rule.text}`;
|
|
9226
|
+
}
|
|
9227
|
+
if (arg.startsWith("rule remove ")) {
|
|
9228
|
+
const id = arg.slice(12).trim();
|
|
9229
|
+
if (id === "") return "usage: /evolve rule remove <id>";
|
|
9230
|
+
const { removed } = await store.removeRule(id);
|
|
9231
|
+
await refreshPromptCache();
|
|
9232
|
+
return removed ? `removed guardrail ${id}` : `no guardrail with id "${id}"`;
|
|
9233
|
+
}
|
|
9079
9234
|
if (arg.startsWith("improve ")) {
|
|
9080
9235
|
const suggestionId = arg.slice(8).trim();
|
|
9081
9236
|
const suggestions = await openWithTrends(100);
|
|
@@ -9147,18 +9302,31 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
9147
9302
|
toolDefinition() {
|
|
9148
9303
|
const inputSchema = z12.object({
|
|
9149
9304
|
suggestionId: z12.string().min(1).describe("Signal id from the evolve suggestions"),
|
|
9150
|
-
implementation: z12.string().min(1).describe("Concise description of the
|
|
9305
|
+
implementation: z12.string().min(1).describe("Concise description of the fix to implement (plugin plan, or the guardrail rule text when kind is prompt_rule)"),
|
|
9306
|
+
kind: z12.enum(["plugin", "prompt_rule"]).optional().describe(
|
|
9307
|
+
"plugin (default): scaffold a fix plugin; prompt_rule: store a guardrail rule that is injected into future system prompts"
|
|
9308
|
+
)
|
|
9151
9309
|
});
|
|
9152
9310
|
return {
|
|
9153
9311
|
name: "evolve_improve",
|
|
9154
|
-
description: "Implement a fix for one repeated tool failure
|
|
9312
|
+
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.",
|
|
9155
9313
|
inputSchema,
|
|
9156
9314
|
paths: () => [],
|
|
9157
9315
|
execute: async (input) => {
|
|
9158
|
-
const { suggestionId, implementation } = input;
|
|
9316
|
+
const { suggestionId, implementation, kind } = input;
|
|
9159
9317
|
const suggestions = await openWithTrends(100);
|
|
9160
9318
|
const suggestion = suggestions.find((item) => item.id === suggestionId);
|
|
9161
9319
|
if (suggestion === void 0) throw new Error(`No open suggestion with id "${suggestionId}".`);
|
|
9320
|
+
if (kind === "prompt_rule") {
|
|
9321
|
+
const { added, rule } = await store.addRule({ text: implementation, sourceId: suggestionId });
|
|
9322
|
+
await store.markSuggestionDone(suggestionId);
|
|
9323
|
+
await refreshPromptCache();
|
|
9324
|
+
return [
|
|
9325
|
+
added ? `Stored guardrail rule [${rule.id}]: ${rule.text}` : `Guardrail already exists [${rule.id}]: ${rule.text}`,
|
|
9326
|
+
`Suggestion [${suggestion.id}] marked done. The rule is injected into the system prompt of future runs.`,
|
|
9327
|
+
"Review rules with /evolve rule list; remove one with /evolve rule remove <id>."
|
|
9328
|
+
].join("\n");
|
|
9329
|
+
}
|
|
9162
9330
|
const name = sanitizePluginName(suggestion.tool);
|
|
9163
9331
|
const dir = await scaffoldFixPlugin(workspace, name);
|
|
9164
9332
|
await writeFile8(join10(dir, "PLAN.md"), buildPlan(suggestion, name, implementation), "utf8");
|
|
@@ -12427,11 +12595,11 @@ Treat the assigned task as self-contained. The caller may not share its full con
|
|
|
12427
12595
|
Own the user's request end to end. Collaborate when a choice materially changes the result, but use sound judgment for ordinary in-scope implementation details. Keep the user informed at meaningful milestones, synthesize any child-agent results yourself, and do not stop while safe, relevant work remains. A forked child receives an explicit Subagent directive after the inherited conversation; in that case, follow that narrower directive for the child assignment.`;
|
|
12428
12596
|
}
|
|
12429
12597
|
function environmentSection(options) {
|
|
12430
|
-
const
|
|
12598
|
+
const git3 = options.environment.isGitRepository === "unknown" ? "unknown" : options.environment.isGitRepository ? "yes" : "no";
|
|
12431
12599
|
return `# Environment
|
|
12432
12600
|
|
|
12433
12601
|
- Working directory: ${data(options.workspace)}
|
|
12434
|
-
- Git repository: ${
|
|
12602
|
+
- Git repository: ${git3}
|
|
12435
12603
|
- Platform: ${data(options.environment.platform)}
|
|
12436
12604
|
- OS version: ${data(options.environment.osVersion)}
|
|
12437
12605
|
- Shell: ${data(options.environment.shell)}`;
|
|
@@ -15396,19 +15564,255 @@ function severityLabel(severity) {
|
|
|
15396
15564
|
}
|
|
15397
15565
|
}
|
|
15398
15566
|
|
|
15399
|
-
// src/
|
|
15567
|
+
// src/git/service.ts
|
|
15568
|
+
async function git2(workspace, args, timeoutMs = 3e4) {
|
|
15569
|
+
const result = await execFileNoThrow("git", ["-C", workspace, ...args], { timeout: timeoutMs, useCwd: false });
|
|
15570
|
+
return { ok: result.code === 0, stdout: result.stdout, stderr: result.stderr, code: result.code };
|
|
15571
|
+
}
|
|
15572
|
+
async function isGitRepository(workspace) {
|
|
15573
|
+
const result = await git2(workspace, ["rev-parse", "--is-inside-work-tree"], 2e3);
|
|
15574
|
+
return result.ok && result.stdout.trim() === "true";
|
|
15575
|
+
}
|
|
15576
|
+
function gitFailure(result, action) {
|
|
15577
|
+
const detail = result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`;
|
|
15578
|
+
return new Error(`git ${action} failed: ${detail.split("\n")[0]}`);
|
|
15579
|
+
}
|
|
15580
|
+
async function changeSummary(workspace) {
|
|
15581
|
+
const branch = await git2(workspace, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
15582
|
+
const head = await git2(workspace, ["rev-parse", "--short", "HEAD"]);
|
|
15583
|
+
const status = await git2(workspace, ["status", "--porcelain"]);
|
|
15584
|
+
if (!status.ok) throw gitFailure(status, "status");
|
|
15585
|
+
const statusLines = status.stdout.split("\n").map((line) => line.trimEnd()).filter((line) => line.length > 0);
|
|
15586
|
+
const stagedFiles = [];
|
|
15587
|
+
const unstagedFiles = [];
|
|
15588
|
+
const untrackedFiles = [];
|
|
15589
|
+
for (const line of statusLines) {
|
|
15590
|
+
const index = line[0] ?? " ";
|
|
15591
|
+
const worktree = line[1] ?? " ";
|
|
15592
|
+
const file = line.slice(3);
|
|
15593
|
+
if (index === "?" && worktree === "?") {
|
|
15594
|
+
untrackedFiles.push(file);
|
|
15595
|
+
continue;
|
|
15596
|
+
}
|
|
15597
|
+
if (index !== " " && index !== "?") stagedFiles.push(file);
|
|
15598
|
+
if (worktree !== " " && worktree !== "?") unstagedFiles.push(file);
|
|
15599
|
+
}
|
|
15600
|
+
return {
|
|
15601
|
+
branch: branch.ok ? branch.stdout.trim() : "HEAD",
|
|
15602
|
+
head: head.ok ? head.stdout.trim() : "unknown",
|
|
15603
|
+
statusLines,
|
|
15604
|
+
stagedFiles: [...new Set(stagedFiles)],
|
|
15605
|
+
unstagedFiles: [...new Set(unstagedFiles)],
|
|
15606
|
+
untrackedFiles
|
|
15607
|
+
};
|
|
15608
|
+
}
|
|
15609
|
+
async function stagedDiff(workspace, maxChars = 96e3) {
|
|
15610
|
+
const stat9 = await git2(workspace, ["diff", "--staged", "--stat"]);
|
|
15611
|
+
const diff = await git2(workspace, ["diff", "--staged", "--no-ext-diff", "--no-color"]);
|
|
15612
|
+
if (!diff.ok) throw gitFailure(diff, "diff --staged");
|
|
15613
|
+
const text = diff.stdout;
|
|
15614
|
+
return {
|
|
15615
|
+
diff: text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
15616
|
+
\u2026[diff truncated]` : text,
|
|
15617
|
+
stat: stat9.stdout.trim(),
|
|
15618
|
+
truncated: text.length > maxChars
|
|
15619
|
+
};
|
|
15620
|
+
}
|
|
15621
|
+
async function uncommittedDiff(workspace, maxChars = 96e3) {
|
|
15622
|
+
const stat9 = await git2(workspace, ["diff", "HEAD", "--stat"]);
|
|
15623
|
+
const diff = await git2(workspace, ["diff", "HEAD", "--no-ext-diff", "--no-color"]);
|
|
15624
|
+
if (!diff.ok && !diff.stderr.includes("unknown revision")) throw gitFailure(diff, "diff HEAD");
|
|
15625
|
+
const summary2 = await changeSummary(workspace);
|
|
15626
|
+
const text = diff.stdout;
|
|
15627
|
+
return {
|
|
15628
|
+
diff: text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
15629
|
+
\u2026[diff truncated]` : text,
|
|
15630
|
+
stat: stat9.stdout.trim(),
|
|
15631
|
+
untracked: summary2.untrackedFiles
|
|
15632
|
+
};
|
|
15633
|
+
}
|
|
15634
|
+
async function stageAll(workspace) {
|
|
15635
|
+
const result = await git2(workspace, ["add", "-A"]);
|
|
15636
|
+
if (!result.ok) throw gitFailure(result, "add");
|
|
15637
|
+
}
|
|
15638
|
+
async function commit(workspace, commitMessage) {
|
|
15639
|
+
const result = await execFileNoThrow("git", ["-C", workspace, "commit", "-F", "-"], {
|
|
15640
|
+
timeout: 3e4,
|
|
15641
|
+
useCwd: false,
|
|
15642
|
+
input: commitMessage
|
|
15643
|
+
});
|
|
15644
|
+
if (result.code !== 0) {
|
|
15645
|
+
const detail = result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`;
|
|
15646
|
+
throw new Error(`git commit failed: ${detail.split("\n")[0]}`);
|
|
15647
|
+
}
|
|
15648
|
+
const head = await git2(workspace, ["log", "-1", "--pretty=%h %s"]);
|
|
15649
|
+
return head.stdout.trim() || "committed";
|
|
15650
|
+
}
|
|
15651
|
+
async function fileHistory(workspace, path, limit) {
|
|
15652
|
+
const args = [
|
|
15653
|
+
"log",
|
|
15654
|
+
`--max-count=${limit}`,
|
|
15655
|
+
"--date=short",
|
|
15656
|
+
"--pretty=format:%h%x1f%ad%x1f%an%x1f%s",
|
|
15657
|
+
...path === void 0 ? [] : ["--follow", "--", path]
|
|
15658
|
+
];
|
|
15659
|
+
const result = await git2(workspace, args, 15e3);
|
|
15660
|
+
if (!result.ok) throw gitFailure(result, "log");
|
|
15661
|
+
return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((line) => {
|
|
15662
|
+
const [hash = "", date = "", author = "", ...subject] = line.split("");
|
|
15663
|
+
return { hash, date, author, subject: subject.join("") };
|
|
15664
|
+
});
|
|
15665
|
+
}
|
|
15666
|
+
async function gitMarker(workspace) {
|
|
15667
|
+
try {
|
|
15668
|
+
if (!await isGitRepository(workspace)) return void 0;
|
|
15669
|
+
const summary2 = await changeSummary(workspace);
|
|
15670
|
+
if (summary2.head === "unknown") return void 0;
|
|
15671
|
+
const dirty = summary2.statusLines.length > 0 ? "-dirty" : "";
|
|
15672
|
+
return `${summary2.branch}@${summary2.head}${dirty}`;
|
|
15673
|
+
} catch {
|
|
15674
|
+
return void 0;
|
|
15675
|
+
}
|
|
15676
|
+
}
|
|
15677
|
+
|
|
15678
|
+
// src/git/insights.ts
|
|
15400
15679
|
import { z as z24 } from "zod";
|
|
15401
|
-
|
|
15402
|
-
|
|
15403
|
-
|
|
15680
|
+
async function suggestCommitMessage(options, input, signal) {
|
|
15681
|
+
const prompt = [
|
|
15682
|
+
"Write a git commit message for the staged changes below.",
|
|
15683
|
+
"Rules: Conventional Commits style (feat/fix/refactor/docs/test/chore scope); a short imperative subject line <= 72 chars; an optional body only when the change needs explanation; no code fences; reply with the commit message only.",
|
|
15684
|
+
...input.hint === void 0 || input.hint.trim() === "" ? [] : [`The user added this context: ${input.hint.trim()}`],
|
|
15685
|
+
"",
|
|
15686
|
+
`Change summary:
|
|
15687
|
+
${input.stat}`,
|
|
15688
|
+
"",
|
|
15689
|
+
`Diff:
|
|
15690
|
+
${input.diff}`
|
|
15691
|
+
].join("\n");
|
|
15692
|
+
const { adapter, model } = options.registry.get(options.modelId());
|
|
15693
|
+
let text = "";
|
|
15694
|
+
for await (const event of adapter.stream({
|
|
15695
|
+
model,
|
|
15696
|
+
messages: [{ role: "user", content: prompt }],
|
|
15697
|
+
tools: [],
|
|
15698
|
+
signal
|
|
15699
|
+
})) {
|
|
15700
|
+
if (event.type === "text") text += event.text;
|
|
15701
|
+
else if (event.type === "error") throw new Error(`commit message generation failed: ${event.error.message}`);
|
|
15702
|
+
else if (event.type === "done") break;
|
|
15703
|
+
}
|
|
15704
|
+
const cleaned = cleanCommitMessage(text);
|
|
15705
|
+
if (cleaned === "") throw new Error("commit message generation returned no text");
|
|
15706
|
+
return cleaned;
|
|
15707
|
+
}
|
|
15708
|
+
function cleanCommitMessage(raw) {
|
|
15709
|
+
const trimmed = raw.trim().replace(/^```[a-z]*\s*/iu, "").replace(/```$/u, "").trim();
|
|
15710
|
+
const lines = trimmed.split("\n");
|
|
15711
|
+
const kept = [];
|
|
15712
|
+
for (const line of lines) {
|
|
15713
|
+
if (/^(here('|’)s|notes?:|explanation:)/iu.test(line.trim()) && kept.length > 0) break;
|
|
15714
|
+
kept.push(line);
|
|
15715
|
+
}
|
|
15716
|
+
return kept.join("\n").trim().slice(0, 2e3);
|
|
15717
|
+
}
|
|
15718
|
+
var ReviewFindingSchema = z24.object({
|
|
15719
|
+
severity: z24.enum(["critical", "warning", "nit"]),
|
|
15720
|
+
file: z24.string(),
|
|
15721
|
+
line: z24.string().describe('Line number or hunk header the finding refers to, e.g. "42" or "@@ -10,4 +10,6 @@"'),
|
|
15722
|
+
issue: z24.string(),
|
|
15723
|
+
suggestion: z24.string()
|
|
15724
|
+
}).strict();
|
|
15725
|
+
var ReviewReportSchema = z24.object({
|
|
15726
|
+
summary: z24.string().describe("One-paragraph overall assessment of the change"),
|
|
15727
|
+
verdict: z24.enum(["ship", "ship-with-fixes", "needs-work"]),
|
|
15728
|
+
findings: z24.array(ReviewFindingSchema).max(20)
|
|
15729
|
+
}).strict();
|
|
15730
|
+
async function reviewDiff(options, input, signal) {
|
|
15731
|
+
const prompt = [
|
|
15732
|
+
"You are reviewing uncommitted local changes before the user commits them.",
|
|
15733
|
+
"Focus on: correctness bugs, broken error handling, security issues (secrets, injection, path traversal), missing edge cases, and obvious test gaps.",
|
|
15734
|
+
"Do not comment on formatting or style unless it hides a bug. Report at most the 10 most important findings.",
|
|
15735
|
+
...input.focus === void 0 || input.focus.trim() === "" ? [] : [`The user specifically wants attention on: ${input.focus.trim()}`],
|
|
15736
|
+
...input.untracked.length === 0 ? [] : [`Untracked files not shown in the diff: ${input.untracked.join(", ")}`],
|
|
15737
|
+
"",
|
|
15738
|
+
`Change summary:
|
|
15739
|
+
${input.stat}`,
|
|
15740
|
+
"",
|
|
15741
|
+
`Diff:
|
|
15742
|
+
${input.diff}`
|
|
15743
|
+
].join("\n");
|
|
15744
|
+
const structured = withStructuredOutput({
|
|
15745
|
+
registry: options.registry,
|
|
15746
|
+
modelId: options.modelId(),
|
|
15747
|
+
name: "report_review",
|
|
15748
|
+
description: "Structured review report for uncommitted changes",
|
|
15749
|
+
schema: ReviewReportSchema
|
|
15750
|
+
});
|
|
15751
|
+
const { value } = await structured.invoke({
|
|
15752
|
+
messages: [{ role: "user", content: prompt }],
|
|
15753
|
+
signal
|
|
15754
|
+
});
|
|
15755
|
+
return value;
|
|
15756
|
+
}
|
|
15757
|
+
function formatReviewReport(report) {
|
|
15758
|
+
const lines = [`Review verdict: ${report.verdict}`, "", report.summary.trim()];
|
|
15759
|
+
if (report.findings.length > 0) {
|
|
15760
|
+
lines.push("", "Findings:");
|
|
15761
|
+
for (const finding of report.findings) {
|
|
15762
|
+
lines.push(`- [${finding.severity}] ${finding.file}${finding.line === "" ? "" : `:${finding.line}`} \u2014 ${finding.issue}`);
|
|
15763
|
+
if (finding.suggestion.trim() !== "") lines.push(` fix: ${finding.suggestion}`);
|
|
15764
|
+
}
|
|
15765
|
+
} else {
|
|
15766
|
+
lines.push("", "No issues found.");
|
|
15767
|
+
}
|
|
15768
|
+
return lines.join("\n");
|
|
15769
|
+
}
|
|
15770
|
+
|
|
15771
|
+
// src/git/tools.ts
|
|
15772
|
+
import { z as z25 } from "zod";
|
|
15773
|
+
var GitHistoryInput = z25.object({
|
|
15774
|
+
path: z25.string().describe("File to inspect; omit for the whole repository history").optional(),
|
|
15775
|
+
limit: z25.number().int().min(1).max(50).describe("Maximum number of commits to return (default 20)").optional()
|
|
15404
15776
|
});
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15777
|
+
function createGitHistoryTool(workspace) {
|
|
15778
|
+
return {
|
|
15779
|
+
name: "GitHistory",
|
|
15780
|
+
description: "Show git commit history for the repository or for one file (follows renames). Use to understand when and why code changed.",
|
|
15781
|
+
inputSchema: GitHistoryInput,
|
|
15782
|
+
readOnly: true,
|
|
15783
|
+
paths: (input) => input.path === void 0 ? [] : [input.path],
|
|
15784
|
+
summarize: (input) => input.path ?? "repository",
|
|
15785
|
+
renderForModel: (output) => output,
|
|
15786
|
+
async execute(input, signal) {
|
|
15787
|
+
signal.throwIfAborted();
|
|
15788
|
+
if (!await isGitRepository(workspace)) throw new Error("Not a git repository");
|
|
15789
|
+
const limit = input.limit ?? 20;
|
|
15790
|
+
const entries = await fileHistory(workspace, input.path, limit);
|
|
15791
|
+
if (entries.length === 0) {
|
|
15792
|
+
return input.path === void 0 ? "No commits found." : `No commits found for ${input.path} (the file may be untracked).`;
|
|
15793
|
+
}
|
|
15794
|
+
const scope2 = input.path === void 0 ? "repository" : input.path;
|
|
15795
|
+
return [
|
|
15796
|
+
`git history for ${scope2} (${entries.length} commits):`,
|
|
15797
|
+
...entries.map((entry2) => `${entry2.hash} ${entry2.date} ${entry2.author}: ${entry2.subject}`)
|
|
15798
|
+
].join("\n");
|
|
15799
|
+
}
|
|
15800
|
+
};
|
|
15801
|
+
}
|
|
15802
|
+
|
|
15803
|
+
// src/tools/ask-user-question.ts
|
|
15804
|
+
import { z as z26 } from "zod";
|
|
15805
|
+
var QuestionOptionSchema = z26.object({
|
|
15806
|
+
label: z26.string().trim().min(1),
|
|
15807
|
+
description: z26.string().trim().min(1)
|
|
15808
|
+
});
|
|
15809
|
+
var QuestionSchema = z26.object({
|
|
15810
|
+
question: z26.string().trim().min(1),
|
|
15811
|
+
header: z26.string().trim().min(1),
|
|
15812
|
+
options: z26.array(QuestionOptionSchema).min(1).max(4)
|
|
15409
15813
|
});
|
|
15410
|
-
var AskUserQuestionInput =
|
|
15411
|
-
questions:
|
|
15814
|
+
var AskUserQuestionInput = z26.object({
|
|
15815
|
+
questions: z26.array(QuestionSchema).min(1).max(4)
|
|
15412
15816
|
});
|
|
15413
15817
|
function hookAnswersFromUpdatedInput(updatedInput, questions) {
|
|
15414
15818
|
if (typeof updatedInput !== "object" || updatedInput === null) return void 0;
|
|
@@ -15492,20 +15896,20 @@ function createAskUserQuestionTool(handler) {
|
|
|
15492
15896
|
}
|
|
15493
15897
|
|
|
15494
15898
|
// src/tools/todo-write.ts
|
|
15495
|
-
import { z as
|
|
15496
|
-
var TodoStatusSchema =
|
|
15899
|
+
import { z as z27 } from "zod";
|
|
15900
|
+
var TodoStatusSchema = z27.enum([
|
|
15497
15901
|
"pending",
|
|
15498
15902
|
"in_progress",
|
|
15499
15903
|
"completed",
|
|
15500
15904
|
"cancelled"
|
|
15501
15905
|
]);
|
|
15502
|
-
var TodoItemSchema =
|
|
15503
|
-
content:
|
|
15906
|
+
var TodoItemSchema = z27.object({
|
|
15907
|
+
content: z27.string().trim().min(1),
|
|
15504
15908
|
status: TodoStatusSchema,
|
|
15505
|
-
activeForm:
|
|
15909
|
+
activeForm: z27.string().trim().min(1)
|
|
15506
15910
|
});
|
|
15507
|
-
var TodoWriteInput =
|
|
15508
|
-
todos:
|
|
15911
|
+
var TodoWriteInput = z27.object({
|
|
15912
|
+
todos: z27.array(TodoItemSchema).min(1).max(50)
|
|
15509
15913
|
});
|
|
15510
15914
|
function createTodoWriteTool() {
|
|
15511
15915
|
return {
|
|
@@ -15542,36 +15946,36 @@ function createTodoWriteTool() {
|
|
|
15542
15946
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
15543
15947
|
import { link as link2, lstat as lstat11, mkdir as mkdir17, readFile as readFile17, readdir as readdir9, unlink as unlink4, writeFile as writeFile10 } from "fs/promises";
|
|
15544
15948
|
import { dirname as dirname17, join as join19, resolve as resolve20 } from "path";
|
|
15545
|
-
import { z as
|
|
15949
|
+
import { z as z28 } from "zod";
|
|
15546
15950
|
var MANAGED_TOOL_VERSION = 1;
|
|
15547
15951
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
15548
15952
|
var TOOL_NAME = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
15549
|
-
var JsonSchemaObject =
|
|
15953
|
+
var JsonSchemaObject = z28.record(z28.string(), z28.unknown()).superRefine((schema, context) => {
|
|
15550
15954
|
if (schema.type !== "object") {
|
|
15551
15955
|
context.addIssue({ code: "custom", message: "Tool input JSON Schema must have type object" });
|
|
15552
15956
|
}
|
|
15553
15957
|
});
|
|
15554
|
-
var RegisterManagedToolInputSchema =
|
|
15555
|
-
name:
|
|
15556
|
-
description:
|
|
15958
|
+
var RegisterManagedToolInputSchema = z28.object({
|
|
15959
|
+
name: z28.string().regex(TOOL_NAME).describe("Unique callable tool name using letters, digits, and underscores"),
|
|
15960
|
+
description: z28.string().min(1).max(4096).describe("Purpose and appropriate use of the generated tool"),
|
|
15557
15961
|
inputSchema: JsonSchemaObject.describe("Provider-facing JSON Schema with an object at the root"),
|
|
15558
|
-
implementation:
|
|
15962
|
+
implementation: z28.string().min(1).max(64 * 1024).describe(
|
|
15559
15963
|
"Async JavaScript function body, or complete function/arrow expression, receiving input, signal, and context; return the tool result"
|
|
15560
15964
|
),
|
|
15561
|
-
scope:
|
|
15965
|
+
scope: z28.enum(["project", "global"]).default("project").describe(
|
|
15562
15966
|
"Project persists in the current workspace; global persists for every workspace"
|
|
15563
15967
|
),
|
|
15564
|
-
agents:
|
|
15968
|
+
agents: z28.array(z28.enum(["main", "subagent"])).min(1).max(2).optional().describe(
|
|
15565
15969
|
"Agent roles allowed to call the generated tool; omit to allow both"
|
|
15566
15970
|
)
|
|
15567
15971
|
}).strict();
|
|
15568
|
-
var RemoveManagedToolInputSchema =
|
|
15569
|
-
name:
|
|
15570
|
-
scope:
|
|
15972
|
+
var RemoveManagedToolInputSchema = z28.object({
|
|
15973
|
+
name: z28.string().regex(TOOL_NAME).describe("Managed tool name to remove"),
|
|
15974
|
+
scope: z28.enum(["project", "global"]).optional().describe("Required only when the same name exists in both scopes")
|
|
15571
15975
|
}).strict();
|
|
15572
15976
|
var ManagedToolRecordSchema = RegisterManagedToolInputSchema.extend({
|
|
15573
|
-
version:
|
|
15574
|
-
createdAt:
|
|
15977
|
+
version: z28.literal(MANAGED_TOOL_VERSION),
|
|
15978
|
+
createdAt: z28.string().datetime()
|
|
15575
15979
|
}).strict();
|
|
15576
15980
|
var AsyncFunction = Object.getPrototypeOf(async function() {
|
|
15577
15981
|
}).constructor;
|
|
@@ -15758,7 +16162,7 @@ function createManagedToolManagementTools(options) {
|
|
|
15758
16162
|
const listTools = {
|
|
15759
16163
|
name: "ListRegisteredTools",
|
|
15760
16164
|
description: "List durable tools created through RegisterTool, including scope, path, and active precedence",
|
|
15761
|
-
inputSchema:
|
|
16165
|
+
inputSchema: z28.object({}).strict(),
|
|
15762
16166
|
agents: mainOnly,
|
|
15763
16167
|
paths: () => [],
|
|
15764
16168
|
execute: async (_input, signal) => {
|
|
@@ -15771,7 +16175,7 @@ function createManagedToolManagementTools(options) {
|
|
|
15771
16175
|
function compileDefinition(record, scope2, workspace) {
|
|
15772
16176
|
let inputSchema;
|
|
15773
16177
|
try {
|
|
15774
|
-
inputSchema =
|
|
16178
|
+
inputSchema = z28.fromJSONSchema(record.inputSchema);
|
|
15775
16179
|
} catch (error) {
|
|
15776
16180
|
throw new Error(`Invalid input schema for managed tool "${record.name}": ${message(error)}`);
|
|
15777
16181
|
}
|
|
@@ -15871,7 +16275,7 @@ function isMissing4(error) {
|
|
|
15871
16275
|
}
|
|
15872
16276
|
|
|
15873
16277
|
// src/hallucination/confidence.ts
|
|
15874
|
-
import { z as
|
|
16278
|
+
import { z as z29 } from "zod";
|
|
15875
16279
|
|
|
15876
16280
|
// src/hallucination/types.ts
|
|
15877
16281
|
var DEFAULT_SLIDING_WINDOW_SIZE = 20;
|
|
@@ -15884,12 +16288,12 @@ var DEFAULT_EVALUATION_TIMEOUT_MS = 2e3;
|
|
|
15884
16288
|
var MAX_QUERY_CHARS = 5e3;
|
|
15885
16289
|
var MAX_OUTPUT_CHARS = 1e4;
|
|
15886
16290
|
var MAX_EVIDENCE_CHARS = 6e3;
|
|
15887
|
-
var ConfidenceSchema =
|
|
15888
|
-
taskAlignment:
|
|
15889
|
-
evidenceGrounding:
|
|
15890
|
-
processReliability:
|
|
15891
|
-
reason:
|
|
15892
|
-
unsupportedClaims:
|
|
16291
|
+
var ConfidenceSchema = z29.object({
|
|
16292
|
+
taskAlignment: z29.number(),
|
|
16293
|
+
evidenceGrounding: z29.number(),
|
|
16294
|
+
processReliability: z29.number(),
|
|
16295
|
+
reason: z29.string(),
|
|
16296
|
+
unsupportedClaims: z29.array(z29.string()).max(3)
|
|
15893
16297
|
});
|
|
15894
16298
|
var PROMPT = `You are a fast, evidence-aware task-completion verifier.
|
|
15895
16299
|
|
|
@@ -17888,15 +18292,15 @@ function workspacePath(root, input) {
|
|
|
17888
18292
|
}
|
|
17889
18293
|
|
|
17890
18294
|
// src/tools/terminal.ts
|
|
17891
|
-
import { z as
|
|
18295
|
+
import { z as z30 } from "zod";
|
|
17892
18296
|
import { resolve as resolve24 } from "path";
|
|
17893
|
-
var Id =
|
|
17894
|
-
var OpenInput =
|
|
17895
|
-
var OptionalBoolean =
|
|
17896
|
-
var WriteInput2 =
|
|
17897
|
-
var ReadInput3 =
|
|
17898
|
-
var ResizeInput =
|
|
17899
|
-
var CloseInput =
|
|
18297
|
+
var Id = z30.string().regex(/^term-[a-f0-9-]+$/);
|
|
18298
|
+
var OpenInput = z30.object({ cwd: z30.string().optional(), shell: z30.string().optional(), args: z30.array(z30.string()).optional(), columns: z30.coerce.number().int().min(10).max(500).optional(), rows: z30.coerce.number().int().min(2).max(300).optional() });
|
|
18299
|
+
var OptionalBoolean = z30.union([z30.boolean(), z30.string().refine((value) => value === "true" || value === "false")]);
|
|
18300
|
+
var WriteInput2 = z30.object({ id: Id, data: z30.string().max(1e5), enter: OptionalBoolean.optional() });
|
|
18301
|
+
var ReadInput3 = z30.object({ id: Id, cursor: z30.coerce.number().int().min(0).optional() });
|
|
18302
|
+
var ResizeInput = z30.object({ id: Id, columns: z30.coerce.number().int().min(10).max(500), rows: z30.coerce.number().int().min(2).max(300) });
|
|
18303
|
+
var CloseInput = z30.object({ id: Id });
|
|
17900
18304
|
function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
17901
18305
|
const tools = [
|
|
17902
18306
|
{
|
|
@@ -17972,7 +18376,7 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
17972
18376
|
{
|
|
17973
18377
|
name: "TerminalList",
|
|
17974
18378
|
description: "List persistent terminals owned by this agent",
|
|
17975
|
-
inputSchema:
|
|
18379
|
+
inputSchema: z30.object({}),
|
|
17976
18380
|
paths: () => [],
|
|
17977
18381
|
execute: async (_input, _signal, context) => terminals.list(owner(context))
|
|
17978
18382
|
}
|
|
@@ -18351,7 +18755,7 @@ var PalClient = class {
|
|
|
18351
18755
|
};
|
|
18352
18756
|
|
|
18353
18757
|
// src/pals/tools.ts
|
|
18354
|
-
import { z as
|
|
18758
|
+
import { z as z31 } from "zod";
|
|
18355
18759
|
var DEFAULT_SHARE_BUDGET_BYTES = 128 * 1024;
|
|
18356
18760
|
var CollaborationShareGuard = class {
|
|
18357
18761
|
redact;
|
|
@@ -18372,25 +18776,25 @@ var CollaborationShareGuard = class {
|
|
|
18372
18776
|
return redacted;
|
|
18373
18777
|
}
|
|
18374
18778
|
};
|
|
18375
|
-
var Uuid =
|
|
18376
|
-
var Hash =
|
|
18779
|
+
var Uuid = z31.uuid();
|
|
18780
|
+
var Hash = z31.string().regex(/^[a-f0-9]{64}$/);
|
|
18377
18781
|
var Target = PalTargetSchema;
|
|
18378
|
-
var BoundedText =
|
|
18782
|
+
var BoundedText = z31.string().trim().min(1).refine(
|
|
18379
18783
|
(value) => Buffer.byteLength(value, "utf8") <= MAX_MESSAGE_BYTES,
|
|
18380
18784
|
`Text must not exceed ${MAX_MESSAGE_BYTES} UTF-8 bytes`
|
|
18381
18785
|
);
|
|
18382
|
-
var CompletionEvidence =
|
|
18786
|
+
var CompletionEvidence = z31.string().trim().min(1).refine(
|
|
18383
18787
|
(value) => Buffer.byteLength(value, "utf8") <= MAX_COMPLETION_DETAIL_BYTES,
|
|
18384
18788
|
`Completion evidence must not exceed ${MAX_COMPLETION_DETAIL_BYTES} UTF-8 bytes`
|
|
18385
18789
|
);
|
|
18386
|
-
var EmptyInput =
|
|
18387
|
-
var StateInput =
|
|
18388
|
-
var TokenInput =
|
|
18389
|
-
var SendInput =
|
|
18390
|
-
var PlanInput =
|
|
18391
|
-
var ProgressInput =
|
|
18392
|
-
var CompleteInput = TokenInput.extend({ passed:
|
|
18393
|
-
var IntegrateInput = TokenInput.extend({ passed:
|
|
18790
|
+
var EmptyInput = z31.object({}).strict();
|
|
18791
|
+
var StateInput = z31.object({ coWorkId: Uuid }).strict();
|
|
18792
|
+
var TokenInput = z31.object({ coWorkId: Uuid, epoch: z31.number().int().positive(), planHash: Hash }).strict();
|
|
18793
|
+
var SendInput = z31.object({ target: Target, message: BoundedText, coWorkId: Uuid.optional() }).strict();
|
|
18794
|
+
var PlanInput = z31.object({ plan: CoWorkPlanSchema }).strict();
|
|
18795
|
+
var ProgressInput = z31.object({ coWorkId: Uuid, target: Target, detail: BoundedText }).strict();
|
|
18796
|
+
var CompleteInput = TokenInput.extend({ passed: z31.boolean(), detail: CompletionEvidence }).strict();
|
|
18797
|
+
var IntegrateInput = TokenInput.extend({ passed: z31.boolean(), evidence: CompletionEvidence }).strict();
|
|
18394
18798
|
var sharedBytes = (...values) => values.reduce((total, value) => total + Buffer.byteLength(value, "utf8"), 0);
|
|
18395
18799
|
function resolvePresence(presences, target) {
|
|
18396
18800
|
const normalized = normalizePalIdentity(target);
|
|
@@ -18743,6 +19147,7 @@ async function createProductionRuntime(options) {
|
|
|
18743
19147
|
createApplyPatchTool(workspace, fileMutationOptions),
|
|
18744
19148
|
createGlobTool(workspace),
|
|
18745
19149
|
createGrepTool(workspace),
|
|
19150
|
+
createGitHistoryTool(workspace),
|
|
18746
19151
|
createShellTool(workspace, {
|
|
18747
19152
|
jobs,
|
|
18748
19153
|
...executionEnvironment === void 0 ? {} : { executionEnvironment }
|
|
@@ -19119,7 +19524,7 @@ async function createProductionRuntime(options) {
|
|
|
19119
19524
|
})) tools.push(tool);
|
|
19120
19525
|
const taskTool = {
|
|
19121
19526
|
name: "Task",
|
|
19122
|
-
description: "Validate a task graph and execute its nodes with isolated child agents",
|
|
19527
|
+
description: "Validate a task graph and execute its nodes with isolated child agents. Declare each node's `files` (the workspace files it may create or modify) whenever a task writes files; nodes with overlapping files are serialized automatically to prevent concurrent write conflicts.",
|
|
19123
19528
|
inputSchema: TaskGraphSchema,
|
|
19124
19529
|
paths: () => [],
|
|
19125
19530
|
execute: async (input, signal) => {
|
|
@@ -19807,6 +20212,19 @@ async function createProductionRuntime(options) {
|
|
|
19807
20212
|
hooksStatus: () => HOOK_EVENT_NAMES.map((name) => ({ name, pluginHandlers: pluginHooks.filter((item) => item === name).length })),
|
|
19808
20213
|
tasks: () => ({ plan: taskPlan, graph: taskGraph, states: taskStates, results: taskResults }),
|
|
19809
20214
|
evolve: (args) => evolveService.handleCommand(args),
|
|
20215
|
+
gitCommit: (hint, signal) => runGitCommit({
|
|
20216
|
+
workspace,
|
|
20217
|
+
registry,
|
|
20218
|
+
questions,
|
|
20219
|
+
modelId: () => harness.subagentModelId,
|
|
20220
|
+
notify: (message2) => emitOutput({ type: "notice", message: message2 })
|
|
20221
|
+
}, hint, signal),
|
|
20222
|
+
gitReview: (focus, signal) => runGitReview({
|
|
20223
|
+
workspace,
|
|
20224
|
+
registry,
|
|
20225
|
+
modelId: () => harness.subagentModelId,
|
|
20226
|
+
notify: (message2) => emitOutput({ type: "notice", message: message2 })
|
|
20227
|
+
}, focus, signal),
|
|
19810
20228
|
audit: async (toolFilter) => {
|
|
19811
20229
|
try {
|
|
19812
20230
|
const raw = await readFile20(auditLogger.path, "utf8");
|
|
@@ -19894,7 +20312,11 @@ ${summary2}`;
|
|
|
19894
20312
|
},
|
|
19895
20313
|
ide: () => ide.status(),
|
|
19896
20314
|
ideContext: () => ide.editorContext(),
|
|
19897
|
-
checkpoint: (label) =>
|
|
20315
|
+
checkpoint: async (label) => {
|
|
20316
|
+
const marker = await gitMarker(workspace);
|
|
20317
|
+
const tagged = marker === void 0 ? label : label === void 0 ? `git: ${marker}` : `${label} (git: ${marker})`;
|
|
20318
|
+
return sessionHistory.checkpoint(tagged, harness.main.context.snapshot());
|
|
20319
|
+
},
|
|
19898
20320
|
tree: () => sessionHistory.tree(),
|
|
19899
20321
|
rewind: async (nodeId) => {
|
|
19900
20322
|
await sessionHistory.rewind(nodeId, harness.main.context.snapshot());
|
|
@@ -20326,6 +20748,70 @@ async function* persistEach(source, persist) {
|
|
|
20326
20748
|
await persist();
|
|
20327
20749
|
}
|
|
20328
20750
|
}
|
|
20751
|
+
async function runGitCommit(deps, hint, signal) {
|
|
20752
|
+
if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
|
|
20753
|
+
let summary2 = await changeSummary(deps.workspace);
|
|
20754
|
+
if (summary2.statusLines.length === 0) return "Working tree clean \u2014 nothing to commit.";
|
|
20755
|
+
if (summary2.stagedFiles.length === 0) {
|
|
20756
|
+
if (deps.questions === void 0) throw new Error("Nothing is staged. Run `git add` on the files you want to commit first.");
|
|
20757
|
+
const stageAnswers = await deps.questions.ask([{
|
|
20758
|
+
header: "Staging",
|
|
20759
|
+
question: `Nothing is staged, but ${summary2.statusLines.length} change(s) exist. Stage everything before committing?`,
|
|
20760
|
+
options: [
|
|
20761
|
+
{ label: "Stage all", description: "Run git add -A, then generate the commit message." },
|
|
20762
|
+
{ label: "Cancel", description: "Abort /commit and leave the working tree as-is." }
|
|
20763
|
+
]
|
|
20764
|
+
}], signal);
|
|
20765
|
+
if (stageAnswers[0] !== "Stage all") return "/commit cancelled \u2014 nothing was staged.";
|
|
20766
|
+
await stageAll(deps.workspace);
|
|
20767
|
+
summary2 = await changeSummary(deps.workspace);
|
|
20768
|
+
if (summary2.stagedFiles.length === 0) return "Nothing ended up staged \u2014 /commit aborted.";
|
|
20769
|
+
}
|
|
20770
|
+
const staged = await stagedDiff(deps.workspace);
|
|
20771
|
+
signal.throwIfAborted();
|
|
20772
|
+
deps.notify?.("Generating commit message\u2026");
|
|
20773
|
+
let commitMessage;
|
|
20774
|
+
try {
|
|
20775
|
+
commitMessage = await suggestCommitMessage(
|
|
20776
|
+
{ registry: deps.registry, modelId: deps.modelId },
|
|
20777
|
+
{ stat: staged.stat, diff: staged.diff, ...hint === void 0 ? {} : { hint } },
|
|
20778
|
+
signal
|
|
20779
|
+
);
|
|
20780
|
+
} catch (error) {
|
|
20781
|
+
signal.throwIfAborted();
|
|
20782
|
+
const scope2 = summary2.stagedFiles.length === 1 ? summary2.stagedFiles[0] ?? "1 file" : `${summary2.stagedFiles.length} files`;
|
|
20783
|
+
commitMessage = `chore: update ${scope2}`;
|
|
20784
|
+
deps.notify?.(`Commit message generation failed (${message(error)}); using a fallback message.`);
|
|
20785
|
+
}
|
|
20786
|
+
deps.notify?.(`Proposed commit message:
|
|
20787
|
+
${commitMessage}`);
|
|
20788
|
+
if (deps.questions !== void 0) {
|
|
20789
|
+
const confirmAnswers = await deps.questions.ask([{
|
|
20790
|
+
header: "Commit",
|
|
20791
|
+
question: `Commit ${summary2.stagedFiles.length} staged file(s) on ${summary2.branch} with the message above?`,
|
|
20792
|
+
options: [
|
|
20793
|
+
{ label: "Commit", description: "Run git commit with the proposed message." },
|
|
20794
|
+
{ label: "Cancel", description: "Abort; the staged changes stay as-is." }
|
|
20795
|
+
]
|
|
20796
|
+
}], signal);
|
|
20797
|
+
if (confirmAnswers[0] !== "Commit") return "/commit cancelled \u2014 staged changes left untouched.";
|
|
20798
|
+
}
|
|
20799
|
+
const result = await commit(deps.workspace, commitMessage);
|
|
20800
|
+
return `Committed: ${result}`;
|
|
20801
|
+
}
|
|
20802
|
+
async function runGitReview(deps, focus, signal) {
|
|
20803
|
+
if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /review needs git.";
|
|
20804
|
+
const changes = await uncommittedDiff(deps.workspace);
|
|
20805
|
+
if (changes.diff.trim() === "" && changes.untracked.length === 0) return "No uncommitted changes to review.";
|
|
20806
|
+
signal.throwIfAborted();
|
|
20807
|
+
deps.notify?.("Reviewing uncommitted changes\u2026");
|
|
20808
|
+
const report = await reviewDiff(
|
|
20809
|
+
{ registry: deps.registry, modelId: deps.modelId },
|
|
20810
|
+
{ stat: changes.stat, diff: changes.diff, untracked: changes.untracked, ...focus === void 0 ? {} : { focus } },
|
|
20811
|
+
signal
|
|
20812
|
+
);
|
|
20813
|
+
return formatReviewReport(report);
|
|
20814
|
+
}
|
|
20329
20815
|
async function runChild(harness, skills, task, attempt, signal, parentContext) {
|
|
20330
20816
|
return harness.runSubagent(task, async (child, childSignal) => {
|
|
20331
20817
|
const skill = await skills.match(task.description);
|
|
@@ -20338,6 +20824,7 @@ ${await skills.loadBody(skill)}`;
|
|
|
20338
20824
|
`Complete task ${task.id}: ${task.description}`,
|
|
20339
20825
|
`Expected outputs: ${task.expectedOutputs.join("; ")}`,
|
|
20340
20826
|
`Verification: ${task.verification.join("; ")}`,
|
|
20827
|
+
...task.files === void 0 || task.files.length === 0 ? [] : [`Owned files: ${task.files.join(", ")}. Restrict your file writes to these paths; concurrent tasks own everything else.`],
|
|
20341
20828
|
`For completed work, finish by calling TaskOutput. Otherwise return only JSON matching these fields: ${Object.keys(SubagentResultSchema.shape).join(", ")}.${repair}`
|
|
20342
20829
|
].join("\n");
|
|
20343
20830
|
for await (const event of child.loop.run({ prompt, signal: childSignal })) {
|