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.
@@ -1,7 +1,7 @@
1
1
  // src/desktop/main.ts
2
2
  import { appendFileSync } from "fs";
3
3
  import { readFile as readFile34, stat as stat15, writeFile as writeFile18 } from "fs/promises";
4
- import { dirname as dirname23, join as join40 } from "path";
4
+ import { dirname as dirname23, join as join41 } from "path";
5
5
  import { fileURLToPath as fileURLToPath4 } from "url";
6
6
  import { app, BrowserWindow as BrowserWindow2, dialog, ipcMain, Menu, nativeImage, shell } from "electron";
7
7
 
@@ -2611,11 +2611,11 @@ function hexToLab(hex) {
2611
2611
  const b = linearize(channels[2]);
2612
2612
  const x = (0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / 0.95047;
2613
2613
  const y = 0.2126729 * r + 0.7151522 * g + 0.072175 * b;
2614
- const z41 = (0.0193339 * r + 0.119192 * g + 0.9503041 * b) / 1.08883;
2614
+ const z43 = (0.0193339 * r + 0.119192 * g + 0.9503041 * b) / 1.08883;
2615
2615
  const f = (t) => t > 8856e-6 ? Math.cbrt(t) : 7.787 * t + 16 / 116;
2616
2616
  const fx = f(x);
2617
2617
  const fy = f(y);
2618
- const fz = f(z41);
2618
+ const fz = f(z43);
2619
2619
  return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)];
2620
2620
  }
2621
2621
  function deltaE76(left, right) {
@@ -4230,7 +4230,10 @@ var TaskNodeSchema = z9.object({
4230
4230
  description: z9.string().trim().min(1),
4231
4231
  dependencies: z9.array(z9.string().trim().min(1)),
4232
4232
  expectedOutputs: z9.array(z9.string().trim().min(1)),
4233
- verification: z9.array(z9.string().trim().min(1))
4233
+ verification: z9.array(z9.string().trim().min(1)),
4234
+ files: z9.array(z9.string().trim().min(1)).describe(
4235
+ "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."
4236
+ ).optional()
4234
4237
  }).strict();
4235
4238
  var TaskGraphSchema = z9.object({
4236
4239
  nodes: z9.array(TaskNodeSchema)
@@ -4422,6 +4425,7 @@ var SubagentScheduler = class {
4422
4425
  const states = new Map(graph.nodes.map((task) => [task.id, "pending"]));
4423
4426
  const results = /* @__PURE__ */ new Map();
4424
4427
  const running = /* @__PURE__ */ new Map();
4428
+ const runningFiles = /* @__PURE__ */ new Map();
4425
4429
  const started = /* @__PURE__ */ new Set();
4426
4430
  try {
4427
4431
  while (statesHaveWork(states)) {
@@ -4430,7 +4434,9 @@ var SubagentScheduler = class {
4430
4434
  for (const task of graph.nodes) {
4431
4435
  if (running.size >= this.#options.maxSubagents) break;
4432
4436
  if (states.get(task.id) !== "pending" || !isReady(task, states)) continue;
4437
+ if (conflictsWithRunning(task, runningFiles)) continue;
4433
4438
  states.set(task.id, "running");
4439
+ if (task.files !== void 0) runningFiles.set(task.id, task.files.map(normalizeOwnedFile));
4434
4440
  const execution = this.#runTask(task, signal);
4435
4441
  running.set(task.id, execution);
4436
4442
  started.add(execution);
@@ -4443,6 +4449,7 @@ var SubagentScheduler = class {
4443
4449
  const completion = await Promise.race(running.values());
4444
4450
  signal.throwIfAborted();
4445
4451
  running.delete(completion.id);
4452
+ runningFiles.delete(completion.id);
4446
4453
  results.set(completion.id, completion.result);
4447
4454
  states.set(completion.id, completion.result.status);
4448
4455
  await this.#options.onResult?.(completion.result);
@@ -4520,6 +4527,26 @@ var SubagentScheduler = class {
4520
4527
  function isReady(task, states) {
4521
4528
  return task.dependencies.every((dependency) => states.get(dependency) === "completed");
4522
4529
  }
4530
+ function normalizeOwnedFile(value) {
4531
+ return value.replace(/\\/g, "/");
4532
+ }
4533
+ function ownedFilesConflict(left, right) {
4534
+ for (const one of left) {
4535
+ for (const other of right) {
4536
+ if (one === other) return true;
4537
+ if (other.startsWith(`${one}/`) || one.startsWith(`${other}/`)) return true;
4538
+ }
4539
+ }
4540
+ return false;
4541
+ }
4542
+ function conflictsWithRunning(task, runningFiles) {
4543
+ if (task.files === void 0 || task.files.length === 0) return false;
4544
+ const owned = task.files.map(normalizeOwnedFile);
4545
+ for (const files of runningFiles.values()) {
4546
+ if (ownedFilesConflict(owned, files)) return true;
4547
+ }
4548
+ return false;
4549
+ }
4523
4550
  function statesHaveWork(states) {
4524
4551
  return [...states.values()].some((state) => state === "pending" || state === "running");
4525
4552
  }
@@ -8686,23 +8713,45 @@ async function readJsonArray(file) {
8686
8713
  return [];
8687
8714
  }
8688
8715
  }
8716
+ async function readRules(file) {
8717
+ try {
8718
+ const parsed = JSON.parse(await readFile7(file, "utf8"));
8719
+ if (!Array.isArray(parsed)) return [];
8720
+ return parsed.filter((item) => item !== null && typeof item === "object").filter((item) => typeof item.id === "string" && typeof item.text === "string").map((item) => ({
8721
+ id: item.id,
8722
+ text: item.text,
8723
+ addedAt: typeof item.addedAt === "string" ? item.addedAt : "",
8724
+ ...typeof item.sourceId === "string" ? { sourceId: item.sourceId } : {}
8725
+ }));
8726
+ } catch {
8727
+ return [];
8728
+ }
8729
+ }
8730
+ async function writeRules(file, rules) {
8731
+ await mkdir8(dirname7(file), { recursive: true, mode: 448 });
8732
+ await writeFile6(file, JSON.stringify(rules, null, 2), { encoding: "utf8", mode: 384 });
8733
+ }
8689
8734
  var EvolveStore = class {
8690
8735
  dir;
8691
8736
  signalsFile;
8692
8737
  reflectionsFile;
8693
8738
  doneFile;
8694
8739
  verifiedFile;
8740
+ rulesFile;
8695
8741
  maxSignals;
8742
+ maxRules;
8696
8743
  // Serialize file mutations: tool hooks may fire in bursts.
8697
8744
  #queue = Promise.resolve();
8698
8745
  constructor(options) {
8699
- const { workspace, maxSignals = 400 } = options;
8746
+ const { workspace, maxSignals = 400, maxRules = 20 } = options;
8700
8747
  this.dir = join9(workspace, ".flavor", "evolve");
8701
8748
  this.signalsFile = join9(this.dir, "signals.jsonl");
8702
8749
  this.reflectionsFile = join9(this.dir, "reflections.jsonl");
8703
8750
  this.doneFile = join9(this.dir, "done.json");
8704
8751
  this.verifiedFile = join9(this.dir, "verified.json");
8752
+ this.rulesFile = join9(this.dir, "rules.json");
8705
8753
  this.maxSignals = maxSignals;
8754
+ this.maxRules = maxRules;
8706
8755
  }
8707
8756
  #enqueue(operation) {
8708
8757
  const next = this.#queue.then(operation, operation);
@@ -8848,6 +8897,43 @@ var EvolveStore = class {
8848
8897
  }
8849
8898
  });
8850
8899
  }
8900
+ /** Learned guardrail rules, oldest first. */
8901
+ listRules() {
8902
+ return this.#enqueue(() => readRules(this.rulesFile));
8903
+ }
8904
+ /**
8905
+ * Add one guardrail rule. Dedupes by fingerprint of the normalized text and
8906
+ * keeps at most maxRules entries (oldest dropped first).
8907
+ */
8908
+ addRule(input) {
8909
+ return this.#enqueue(async () => {
8910
+ const text = input.text.replace(/\s+/g, " ").trim().slice(0, 300);
8911
+ if (text === "") throw new Error("rule text must not be empty");
8912
+ const id = fingerprint("rule", void 0, text);
8913
+ const rules = await readRules(this.rulesFile);
8914
+ const existing = rules.find((rule2) => rule2.id === id);
8915
+ if (existing !== void 0) return { added: false, rule: existing };
8916
+ const rule = {
8917
+ id,
8918
+ text,
8919
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
8920
+ ...input.sourceId === void 0 ? {} : { sourceId: input.sourceId }
8921
+ };
8922
+ rules.push(rule);
8923
+ if (rules.length > this.maxRules) rules.splice(0, rules.length - this.maxRules);
8924
+ await writeRules(this.rulesFile, rules);
8925
+ return { added: true, rule };
8926
+ });
8927
+ }
8928
+ removeRule(id) {
8929
+ return this.#enqueue(async () => {
8930
+ const rules = await readRules(this.rulesFile);
8931
+ const next = rules.filter((rule) => rule.id !== id);
8932
+ if (next.length === rules.length) return { removed: false };
8933
+ await writeRules(this.rulesFile, next);
8934
+ return { removed: true };
8935
+ });
8936
+ }
8851
8937
  };
8852
8938
 
8853
8939
  // src/evolve/service.ts
@@ -8859,9 +8945,15 @@ a plugin, a memory rule, a tool wrapper, or a safer default), implement it in
8859
8945
  this session when it is in scope; otherwise ignore them. Never act on them
8860
8946
  without running the test suite afterwards.
8861
8947
 
8948
+ `;
8949
+ var GUARDRAILS_SECTION_HEADER = `# learned guardrails (evolve)
8950
+
8951
+ These rules were distilled from repeated tool failures in this workspace.
8952
+ Follow them unless the current task or the user explicitly contradicts one.
8953
+
8862
8954
  `;
8863
8955
  var USAGE = [
8864
- "usage: /evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>|verified|clear>",
8956
+ "usage: /evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>|verified|trends [n]|rule <list|add|remove>|clear>",
8865
8957
  " signals list recent failing tool results",
8866
8958
  " suggest aggregate repeated failures into fix suggestions (trend-aware ordering)",
8867
8959
  " improve scaffold a fix-<tool>/ plugin dir + PLAN.md for one suggestion",
@@ -8871,6 +8963,8 @@ var USAGE = [
8871
8963
  " revert restore the last good snapshot of a plugin",
8872
8964
  " done mark a suggestion as handled (no longer proposed)",
8873
8965
  " verified list suggestions auto-verified by improving failure trends",
8966
+ " trends cross-run dashboard: tool calls, failures, signalDelta, per-tool movement",
8967
+ " rule manage learned guardrails injected into system prompts (list/add <text>/remove <id>)",
8874
8968
  " clear reset signals, done markers, and verified markers"
8875
8969
  ].join("\n");
8876
8970
  function runCommand(command, cwd, timeoutMs) {
@@ -8962,9 +9056,17 @@ function createEvolveService(options) {
8962
9056
  }
8963
9057
  async function refreshPromptCache() {
8964
9058
  try {
8965
- const suggestions = await openWithTrends(promptTop);
8966
- promptCache = suggestions.length === 0 ? void 0 : `${SUGGEST_SECTION_HEADER}${suggestions.map((suggestion) => `- [${suggestion.id}] ${suggestion.hint}`).join("\n")}
8967
- `;
9059
+ const [suggestions, rules] = await Promise.all([openWithTrends(promptTop), store.listRules()]);
9060
+ const sections = [];
9061
+ if (rules.length > 0) {
9062
+ sections.push(`${GUARDRAILS_SECTION_HEADER}${rules.map((rule) => `- ${rule.text}`).join("\n")}
9063
+ `);
9064
+ }
9065
+ if (suggestions.length > 0) {
9066
+ sections.push(`${SUGGEST_SECTION_HEADER}${suggestions.map((suggestion) => `- [${suggestion.id}] ${suggestion.hint}`).join("\n")}
9067
+ `);
9068
+ }
9069
+ promptCache = sections.length === 0 ? void 0 : sections.join("\n");
8968
9070
  } catch (error) {
8969
9071
  logger.warn(`prompt section failed \u2014 ${error instanceof Error ? error.message : String(error)}`);
8970
9072
  }
@@ -9144,6 +9246,41 @@ function createEvolveService(options) {
9144
9246
  return signal === void 0 ? `[${id}] (signal no longer recorded)` : `[${id}] ${signal.tool} x${signal.count} \u2014 ${signal.error} (verified)`;
9145
9247
  }).join("\n");
9146
9248
  }
9249
+ if (arg === "trends" || arg.startsWith("trends ")) {
9250
+ const requested = Number.parseInt(arg.slice(6).trim(), 10);
9251
+ const count = Number.isNaN(requested) ? 5 : Math.min(Math.max(requested, 1), 50);
9252
+ const reflections = await store.reflections(count);
9253
+ if (reflections.length === 0) return "no reflections recorded yet \u2014 trends appear after runs end";
9254
+ const sign = (value) => value > 0 ? `+${value}` : String(value);
9255
+ const lines = [`evolve trends (last ${reflections.length} run(s), newest first)`];
9256
+ for (const reflection of reflections) {
9257
+ 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)})`);
9258
+ const moved = Object.entries(reflection.perTool ?? {}).filter(([, trend]) => trend.delta !== 0);
9259
+ for (const [tool, trend] of moved) {
9260
+ lines.push(` - ${tool}: ${trend.failures} failure(s) this run (${sign(trend.delta)} vs previous)`);
9261
+ }
9262
+ }
9263
+ return lines.join("\n");
9264
+ }
9265
+ if (arg === "rule" || arg === "rule list") {
9266
+ const rules = await store.listRules();
9267
+ if (rules.length === 0) return "no guardrail rules yet \u2014 add one with /evolve rule add <text>";
9268
+ return rules.map((rule) => `[${rule.id}] ${rule.text}`).join("\n");
9269
+ }
9270
+ if (arg.startsWith("rule add ")) {
9271
+ const text = arg.slice(9).trim();
9272
+ if (text === "") return "usage: /evolve rule add <text>";
9273
+ const { added, rule } = await store.addRule({ text });
9274
+ await refreshPromptCache();
9275
+ return added ? `added guardrail [${rule.id}]: ${rule.text}` : `guardrail already exists [${rule.id}]: ${rule.text}`;
9276
+ }
9277
+ if (arg.startsWith("rule remove ")) {
9278
+ const id = arg.slice(12).trim();
9279
+ if (id === "") return "usage: /evolve rule remove <id>";
9280
+ const { removed } = await store.removeRule(id);
9281
+ await refreshPromptCache();
9282
+ return removed ? `removed guardrail ${id}` : `no guardrail with id "${id}"`;
9283
+ }
9147
9284
  if (arg.startsWith("improve ")) {
9148
9285
  const suggestionId = arg.slice(8).trim();
9149
9286
  const suggestions = await openWithTrends(100);
@@ -9215,18 +9352,31 @@ ${result.stderr.slice(-4e3)}` : ""}`;
9215
9352
  toolDefinition() {
9216
9353
  const inputSchema = z17.object({
9217
9354
  suggestionId: z17.string().min(1).describe("Signal id from the evolve suggestions"),
9218
- implementation: z17.string().min(1).describe("Concise description of the plugin fix to implement")
9355
+ implementation: z17.string().min(1).describe("Concise description of the fix to implement (plugin plan, or the guardrail rule text when kind is prompt_rule)"),
9356
+ kind: z17.enum(["plugin", "prompt_rule"]).optional().describe(
9357
+ "plugin (default): scaffold a fix plugin; prompt_rule: store a guardrail rule that is injected into future system prompts"
9358
+ )
9219
9359
  });
9220
9360
  return {
9221
9361
  name: "evolve_improve",
9222
- description: "Implement a fix for one repeated tool failure as a flavor-code plugin: scaffolds the fix-<tool>/ plugin dir, writes PLAN.md, and returns instructions for implementing, verifying, reloading, and testing it. Use when the model proposes a concrete plugin-level fix for a suggestion in the system prompt.",
9362
+ 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.",
9223
9363
  inputSchema,
9224
9364
  paths: () => [],
9225
9365
  execute: async (input) => {
9226
- const { suggestionId, implementation } = input;
9366
+ const { suggestionId, implementation, kind } = input;
9227
9367
  const suggestions = await openWithTrends(100);
9228
9368
  const suggestion = suggestions.find((item) => item.id === suggestionId);
9229
9369
  if (suggestion === void 0) throw new Error(`No open suggestion with id "${suggestionId}".`);
9370
+ if (kind === "prompt_rule") {
9371
+ const { added, rule } = await store.addRule({ text: implementation, sourceId: suggestionId });
9372
+ await store.markSuggestionDone(suggestionId);
9373
+ await refreshPromptCache();
9374
+ return [
9375
+ added ? `Stored guardrail rule [${rule.id}]: ${rule.text}` : `Guardrail already exists [${rule.id}]: ${rule.text}`,
9376
+ `Suggestion [${suggestion.id}] marked done. The rule is injected into the system prompt of future runs.`,
9377
+ "Review rules with /evolve rule list; remove one with /evolve rule remove <id>."
9378
+ ].join("\n");
9379
+ }
9230
9380
  const name = sanitizePluginName(suggestion.tool);
9231
9381
  const dir = await scaffoldFixPlugin(workspace, name);
9232
9382
  await writeFile7(join10(dir, "PLAN.md"), buildPlan(suggestion, name, implementation), "utf8");
@@ -13100,11 +13250,11 @@ Treat the assigned task as self-contained. The caller may not share its full con
13100
13250
  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.`;
13101
13251
  }
13102
13252
  function environmentSection(options) {
13103
- const git2 = options.environment.isGitRepository === "unknown" ? "unknown" : options.environment.isGitRepository ? "yes" : "no";
13253
+ const git3 = options.environment.isGitRepository === "unknown" ? "unknown" : options.environment.isGitRepository ? "yes" : "no";
13104
13254
  return `# Environment
13105
13255
 
13106
13256
  - Working directory: ${data(options.workspace)}
13107
- - Git repository: ${git2}
13257
+ - Git repository: ${git3}
13108
13258
  - Platform: ${data(options.environment.platform)}
13109
13259
  - OS version: ${data(options.environment.osVersion)}
13110
13260
  - Shell: ${data(options.environment.shell)}`;
@@ -17559,19 +17709,255 @@ function severityLabel(severity) {
17559
17709
  }
17560
17710
  }
17561
17711
 
17562
- // src/tools/ask-user-question.ts
17712
+ // src/git/service.ts
17713
+ async function git2(workspace, args, timeoutMs = 3e4) {
17714
+ const result = await execFileNoThrow("git", ["-C", workspace, ...args], { timeout: timeoutMs, useCwd: false });
17715
+ return { ok: result.code === 0, stdout: result.stdout, stderr: result.stderr, code: result.code };
17716
+ }
17717
+ async function isGitRepository(workspace) {
17718
+ const result = await git2(workspace, ["rev-parse", "--is-inside-work-tree"], 2e3);
17719
+ return result.ok && result.stdout.trim() === "true";
17720
+ }
17721
+ function gitFailure(result, action) {
17722
+ const detail = result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`;
17723
+ return new Error(`git ${action} failed: ${detail.split("\n")[0]}`);
17724
+ }
17725
+ async function changeSummary(workspace) {
17726
+ const branch = await git2(workspace, ["rev-parse", "--abbrev-ref", "HEAD"]);
17727
+ const head = await git2(workspace, ["rev-parse", "--short", "HEAD"]);
17728
+ const status = await git2(workspace, ["status", "--porcelain"]);
17729
+ if (!status.ok) throw gitFailure(status, "status");
17730
+ const statusLines = status.stdout.split("\n").map((line) => line.trimEnd()).filter((line) => line.length > 0);
17731
+ const stagedFiles = [];
17732
+ const unstagedFiles = [];
17733
+ const untrackedFiles = [];
17734
+ for (const line of statusLines) {
17735
+ const index = line[0] ?? " ";
17736
+ const worktree = line[1] ?? " ";
17737
+ const file = line.slice(3);
17738
+ if (index === "?" && worktree === "?") {
17739
+ untrackedFiles.push(file);
17740
+ continue;
17741
+ }
17742
+ if (index !== " " && index !== "?") stagedFiles.push(file);
17743
+ if (worktree !== " " && worktree !== "?") unstagedFiles.push(file);
17744
+ }
17745
+ return {
17746
+ branch: branch.ok ? branch.stdout.trim() : "HEAD",
17747
+ head: head.ok ? head.stdout.trim() : "unknown",
17748
+ statusLines,
17749
+ stagedFiles: [...new Set(stagedFiles)],
17750
+ unstagedFiles: [...new Set(unstagedFiles)],
17751
+ untrackedFiles
17752
+ };
17753
+ }
17754
+ async function stagedDiff(workspace, maxChars = 96e3) {
17755
+ const stat16 = await git2(workspace, ["diff", "--staged", "--stat"]);
17756
+ const diff = await git2(workspace, ["diff", "--staged", "--no-ext-diff", "--no-color"]);
17757
+ if (!diff.ok) throw gitFailure(diff, "diff --staged");
17758
+ const text = diff.stdout;
17759
+ return {
17760
+ diff: text.length > maxChars ? `${text.slice(0, maxChars)}
17761
+ \u2026[diff truncated]` : text,
17762
+ stat: stat16.stdout.trim(),
17763
+ truncated: text.length > maxChars
17764
+ };
17765
+ }
17766
+ async function uncommittedDiff(workspace, maxChars = 96e3) {
17767
+ const stat16 = await git2(workspace, ["diff", "HEAD", "--stat"]);
17768
+ const diff = await git2(workspace, ["diff", "HEAD", "--no-ext-diff", "--no-color"]);
17769
+ if (!diff.ok && !diff.stderr.includes("unknown revision")) throw gitFailure(diff, "diff HEAD");
17770
+ const summary2 = await changeSummary(workspace);
17771
+ const text = diff.stdout;
17772
+ return {
17773
+ diff: text.length > maxChars ? `${text.slice(0, maxChars)}
17774
+ \u2026[diff truncated]` : text,
17775
+ stat: stat16.stdout.trim(),
17776
+ untracked: summary2.untrackedFiles
17777
+ };
17778
+ }
17779
+ async function stageAll(workspace) {
17780
+ const result = await git2(workspace, ["add", "-A"]);
17781
+ if (!result.ok) throw gitFailure(result, "add");
17782
+ }
17783
+ async function commit(workspace, commitMessage) {
17784
+ const result = await execFileNoThrow("git", ["-C", workspace, "commit", "-F", "-"], {
17785
+ timeout: 3e4,
17786
+ useCwd: false,
17787
+ input: commitMessage
17788
+ });
17789
+ if (result.code !== 0) {
17790
+ const detail = result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`;
17791
+ throw new Error(`git commit failed: ${detail.split("\n")[0]}`);
17792
+ }
17793
+ const head = await git2(workspace, ["log", "-1", "--pretty=%h %s"]);
17794
+ return head.stdout.trim() || "committed";
17795
+ }
17796
+ async function fileHistory(workspace, path, limit) {
17797
+ const args = [
17798
+ "log",
17799
+ `--max-count=${limit}`,
17800
+ "--date=short",
17801
+ "--pretty=format:%h%x1f%ad%x1f%an%x1f%s",
17802
+ ...path === void 0 ? [] : ["--follow", "--", path]
17803
+ ];
17804
+ const result = await git2(workspace, args, 15e3);
17805
+ if (!result.ok) throw gitFailure(result, "log");
17806
+ return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((line) => {
17807
+ const [hash = "", date = "", author = "", ...subject] = line.split("");
17808
+ return { hash, date, author, subject: subject.join("") };
17809
+ });
17810
+ }
17811
+ async function gitMarker(workspace) {
17812
+ try {
17813
+ if (!await isGitRepository(workspace)) return void 0;
17814
+ const summary2 = await changeSummary(workspace);
17815
+ if (summary2.head === "unknown") return void 0;
17816
+ const dirty = summary2.statusLines.length > 0 ? "-dirty" : "";
17817
+ return `${summary2.branch}@${summary2.head}${dirty}`;
17818
+ } catch {
17819
+ return void 0;
17820
+ }
17821
+ }
17822
+
17823
+ // src/git/insights.ts
17563
17824
  import { z as z30 } from "zod";
17564
- var QuestionOptionSchema = z30.object({
17565
- label: z30.string().trim().min(1),
17566
- description: z30.string().trim().min(1)
17825
+ async function suggestCommitMessage(options, input, signal) {
17826
+ const prompt = [
17827
+ "Write a git commit message for the staged changes below.",
17828
+ "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.",
17829
+ ...input.hint === void 0 || input.hint.trim() === "" ? [] : [`The user added this context: ${input.hint.trim()}`],
17830
+ "",
17831
+ `Change summary:
17832
+ ${input.stat}`,
17833
+ "",
17834
+ `Diff:
17835
+ ${input.diff}`
17836
+ ].join("\n");
17837
+ const { adapter, model } = options.registry.get(options.modelId());
17838
+ let text = "";
17839
+ for await (const event of adapter.stream({
17840
+ model,
17841
+ messages: [{ role: "user", content: prompt }],
17842
+ tools: [],
17843
+ signal
17844
+ })) {
17845
+ if (event.type === "text") text += event.text;
17846
+ else if (event.type === "error") throw new Error(`commit message generation failed: ${event.error.message}`);
17847
+ else if (event.type === "done") break;
17848
+ }
17849
+ const cleaned = cleanCommitMessage(text);
17850
+ if (cleaned === "") throw new Error("commit message generation returned no text");
17851
+ return cleaned;
17852
+ }
17853
+ function cleanCommitMessage(raw) {
17854
+ const trimmed = raw.trim().replace(/^```[a-z]*\s*/iu, "").replace(/```$/u, "").trim();
17855
+ const lines = trimmed.split("\n");
17856
+ const kept = [];
17857
+ for (const line of lines) {
17858
+ if (/^(here('|’)s|notes?:|explanation:)/iu.test(line.trim()) && kept.length > 0) break;
17859
+ kept.push(line);
17860
+ }
17861
+ return kept.join("\n").trim().slice(0, 2e3);
17862
+ }
17863
+ var ReviewFindingSchema = z30.object({
17864
+ severity: z30.enum(["critical", "warning", "nit"]),
17865
+ file: z30.string(),
17866
+ line: z30.string().describe('Line number or hunk header the finding refers to, e.g. "42" or "@@ -10,4 +10,6 @@"'),
17867
+ issue: z30.string(),
17868
+ suggestion: z30.string()
17869
+ }).strict();
17870
+ var ReviewReportSchema = z30.object({
17871
+ summary: z30.string().describe("One-paragraph overall assessment of the change"),
17872
+ verdict: z30.enum(["ship", "ship-with-fixes", "needs-work"]),
17873
+ findings: z30.array(ReviewFindingSchema).max(20)
17874
+ }).strict();
17875
+ async function reviewDiff(options, input, signal) {
17876
+ const prompt = [
17877
+ "You are reviewing uncommitted local changes before the user commits them.",
17878
+ "Focus on: correctness bugs, broken error handling, security issues (secrets, injection, path traversal), missing edge cases, and obvious test gaps.",
17879
+ "Do not comment on formatting or style unless it hides a bug. Report at most the 10 most important findings.",
17880
+ ...input.focus === void 0 || input.focus.trim() === "" ? [] : [`The user specifically wants attention on: ${input.focus.trim()}`],
17881
+ ...input.untracked.length === 0 ? [] : [`Untracked files not shown in the diff: ${input.untracked.join(", ")}`],
17882
+ "",
17883
+ `Change summary:
17884
+ ${input.stat}`,
17885
+ "",
17886
+ `Diff:
17887
+ ${input.diff}`
17888
+ ].join("\n");
17889
+ const structured = withStructuredOutput({
17890
+ registry: options.registry,
17891
+ modelId: options.modelId(),
17892
+ name: "report_review",
17893
+ description: "Structured review report for uncommitted changes",
17894
+ schema: ReviewReportSchema
17895
+ });
17896
+ const { value } = await structured.invoke({
17897
+ messages: [{ role: "user", content: prompt }],
17898
+ signal
17899
+ });
17900
+ return value;
17901
+ }
17902
+ function formatReviewReport(report) {
17903
+ const lines = [`Review verdict: ${report.verdict}`, "", report.summary.trim()];
17904
+ if (report.findings.length > 0) {
17905
+ lines.push("", "Findings:");
17906
+ for (const finding of report.findings) {
17907
+ lines.push(`- [${finding.severity}] ${finding.file}${finding.line === "" ? "" : `:${finding.line}`} \u2014 ${finding.issue}`);
17908
+ if (finding.suggestion.trim() !== "") lines.push(` fix: ${finding.suggestion}`);
17909
+ }
17910
+ } else {
17911
+ lines.push("", "No issues found.");
17912
+ }
17913
+ return lines.join("\n");
17914
+ }
17915
+
17916
+ // src/git/tools.ts
17917
+ import { z as z31 } from "zod";
17918
+ var GitHistoryInput = z31.object({
17919
+ path: z31.string().describe("File to inspect; omit for the whole repository history").optional(),
17920
+ limit: z31.number().int().min(1).max(50).describe("Maximum number of commits to return (default 20)").optional()
17567
17921
  });
17568
- var QuestionSchema = z30.object({
17569
- question: z30.string().trim().min(1),
17570
- header: z30.string().trim().min(1),
17571
- options: z30.array(QuestionOptionSchema).min(1).max(4)
17922
+ function createGitHistoryTool(workspace) {
17923
+ return {
17924
+ name: "GitHistory",
17925
+ description: "Show git commit history for the repository or for one file (follows renames). Use to understand when and why code changed.",
17926
+ inputSchema: GitHistoryInput,
17927
+ readOnly: true,
17928
+ paths: (input) => input.path === void 0 ? [] : [input.path],
17929
+ summarize: (input) => input.path ?? "repository",
17930
+ renderForModel: (output) => output,
17931
+ async execute(input, signal) {
17932
+ signal.throwIfAborted();
17933
+ if (!await isGitRepository(workspace)) throw new Error("Not a git repository");
17934
+ const limit = input.limit ?? 20;
17935
+ const entries = await fileHistory(workspace, input.path, limit);
17936
+ if (entries.length === 0) {
17937
+ return input.path === void 0 ? "No commits found." : `No commits found for ${input.path} (the file may be untracked).`;
17938
+ }
17939
+ const scope2 = input.path === void 0 ? "repository" : input.path;
17940
+ return [
17941
+ `git history for ${scope2} (${entries.length} commits):`,
17942
+ ...entries.map((entry2) => `${entry2.hash} ${entry2.date} ${entry2.author}: ${entry2.subject}`)
17943
+ ].join("\n");
17944
+ }
17945
+ };
17946
+ }
17947
+
17948
+ // src/tools/ask-user-question.ts
17949
+ import { z as z32 } from "zod";
17950
+ var QuestionOptionSchema = z32.object({
17951
+ label: z32.string().trim().min(1),
17952
+ description: z32.string().trim().min(1)
17953
+ });
17954
+ var QuestionSchema = z32.object({
17955
+ question: z32.string().trim().min(1),
17956
+ header: z32.string().trim().min(1),
17957
+ options: z32.array(QuestionOptionSchema).min(1).max(4)
17572
17958
  });
17573
- var AskUserQuestionInput = z30.object({
17574
- questions: z30.array(QuestionSchema).min(1).max(4)
17959
+ var AskUserQuestionInput = z32.object({
17960
+ questions: z32.array(QuestionSchema).min(1).max(4)
17575
17961
  });
17576
17962
  function hookAnswersFromUpdatedInput(updatedInput, questions) {
17577
17963
  if (typeof updatedInput !== "object" || updatedInput === null) return void 0;
@@ -17655,20 +18041,20 @@ function createAskUserQuestionTool(handler) {
17655
18041
  }
17656
18042
 
17657
18043
  // src/tools/todo-write.ts
17658
- import { z as z31 } from "zod";
17659
- var TodoStatusSchema = z31.enum([
18044
+ import { z as z33 } from "zod";
18045
+ var TodoStatusSchema = z33.enum([
17660
18046
  "pending",
17661
18047
  "in_progress",
17662
18048
  "completed",
17663
18049
  "cancelled"
17664
18050
  ]);
17665
- var TodoItemSchema = z31.object({
17666
- content: z31.string().trim().min(1),
18051
+ var TodoItemSchema = z33.object({
18052
+ content: z33.string().trim().min(1),
17667
18053
  status: TodoStatusSchema,
17668
- activeForm: z31.string().trim().min(1)
18054
+ activeForm: z33.string().trim().min(1)
17669
18055
  });
17670
- var TodoWriteInput = z31.object({
17671
- todos: z31.array(TodoItemSchema).min(1).max(50)
18056
+ var TodoWriteInput = z33.object({
18057
+ todos: z33.array(TodoItemSchema).min(1).max(50)
17672
18058
  });
17673
18059
  function createTodoWriteTool() {
17674
18060
  return {
@@ -17705,36 +18091,36 @@ function createTodoWriteTool() {
17705
18091
  import { randomUUID as randomUUID14 } from "crypto";
17706
18092
  import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile20, readdir as readdir10, unlink as unlink2, writeFile as writeFile12 } from "fs/promises";
17707
18093
  import { dirname as dirname20, join as join24, resolve as resolve24 } from "path";
17708
- import { z as z32 } from "zod";
18094
+ import { z as z34 } from "zod";
17709
18095
  var MANAGED_TOOL_VERSION = 1;
17710
18096
  var MAX_RECORD_BYTES = 256 * 1024;
17711
18097
  var TOOL_NAME = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
17712
- var JsonSchemaObject = z32.record(z32.string(), z32.unknown()).superRefine((schema, context) => {
18098
+ var JsonSchemaObject = z34.record(z34.string(), z34.unknown()).superRefine((schema, context) => {
17713
18099
  if (schema.type !== "object") {
17714
18100
  context.addIssue({ code: "custom", message: "Tool input JSON Schema must have type object" });
17715
18101
  }
17716
18102
  });
17717
- var RegisterManagedToolInputSchema = z32.object({
17718
- name: z32.string().regex(TOOL_NAME).describe("Unique callable tool name using letters, digits, and underscores"),
17719
- description: z32.string().min(1).max(4096).describe("Purpose and appropriate use of the generated tool"),
18103
+ var RegisterManagedToolInputSchema = z34.object({
18104
+ name: z34.string().regex(TOOL_NAME).describe("Unique callable tool name using letters, digits, and underscores"),
18105
+ description: z34.string().min(1).max(4096).describe("Purpose and appropriate use of the generated tool"),
17720
18106
  inputSchema: JsonSchemaObject.describe("Provider-facing JSON Schema with an object at the root"),
17721
- implementation: z32.string().min(1).max(64 * 1024).describe(
18107
+ implementation: z34.string().min(1).max(64 * 1024).describe(
17722
18108
  "Async JavaScript function body, or complete function/arrow expression, receiving input, signal, and context; return the tool result"
17723
18109
  ),
17724
- scope: z32.enum(["project", "global"]).default("project").describe(
18110
+ scope: z34.enum(["project", "global"]).default("project").describe(
17725
18111
  "Project persists in the current workspace; global persists for every workspace"
17726
18112
  ),
17727
- agents: z32.array(z32.enum(["main", "subagent"])).min(1).max(2).optional().describe(
18113
+ agents: z34.array(z34.enum(["main", "subagent"])).min(1).max(2).optional().describe(
17728
18114
  "Agent roles allowed to call the generated tool; omit to allow both"
17729
18115
  )
17730
18116
  }).strict();
17731
- var RemoveManagedToolInputSchema = z32.object({
17732
- name: z32.string().regex(TOOL_NAME).describe("Managed tool name to remove"),
17733
- scope: z32.enum(["project", "global"]).optional().describe("Required only when the same name exists in both scopes")
18117
+ var RemoveManagedToolInputSchema = z34.object({
18118
+ name: z34.string().regex(TOOL_NAME).describe("Managed tool name to remove"),
18119
+ scope: z34.enum(["project", "global"]).optional().describe("Required only when the same name exists in both scopes")
17734
18120
  }).strict();
17735
18121
  var ManagedToolRecordSchema = RegisterManagedToolInputSchema.extend({
17736
- version: z32.literal(MANAGED_TOOL_VERSION),
17737
- createdAt: z32.string().datetime()
18122
+ version: z34.literal(MANAGED_TOOL_VERSION),
18123
+ createdAt: z34.string().datetime()
17738
18124
  }).strict();
17739
18125
  var AsyncFunction = Object.getPrototypeOf(async function() {
17740
18126
  }).constructor;
@@ -17921,7 +18307,7 @@ function createManagedToolManagementTools(options) {
17921
18307
  const listTools = {
17922
18308
  name: "ListRegisteredTools",
17923
18309
  description: "List durable tools created through RegisterTool, including scope, path, and active precedence",
17924
- inputSchema: z32.object({}).strict(),
18310
+ inputSchema: z34.object({}).strict(),
17925
18311
  agents: mainOnly,
17926
18312
  paths: () => [],
17927
18313
  execute: async (_input, signal) => {
@@ -17934,7 +18320,7 @@ function createManagedToolManagementTools(options) {
17934
18320
  function compileDefinition(record3, scope2, workspace) {
17935
18321
  let inputSchema;
17936
18322
  try {
17937
- inputSchema = z32.fromJSONSchema(record3.inputSchema);
18323
+ inputSchema = z34.fromJSONSchema(record3.inputSchema);
17938
18324
  } catch (error) {
17939
18325
  throw new Error(`Invalid input schema for managed tool "${record3.name}": ${message(error)}`);
17940
18326
  }
@@ -18034,7 +18420,7 @@ function isMissing4(error) {
18034
18420
  }
18035
18421
 
18036
18422
  // src/pals/protocol.ts
18037
- import { z as z33 } from "zod";
18423
+ import { z as z35 } from "zod";
18038
18424
  var PROTOCOL_VERSION = 1;
18039
18425
  var MAX_ALIAS_LENGTH = 64;
18040
18426
  var MAX_MESSAGE_BYTES = 32 * 1024;
@@ -18051,65 +18437,65 @@ var MIN_UUID_PREFIX_LENGTH = 8;
18051
18437
  function normalizePalIdentity(value) {
18052
18438
  return value.trim().toLocaleLowerCase("en-US");
18053
18439
  }
18054
- var VersionSchema = z33.literal(PROTOCOL_VERSION);
18055
- var UuidSchema = z33.uuid();
18056
- var PrintableIdentitySchema = z33.string().trim().min(1).max(MAX_ALIAS_LENGTH).refine(
18440
+ var VersionSchema = z35.literal(PROTOCOL_VERSION);
18441
+ var UuidSchema = z35.uuid();
18442
+ var PrintableIdentitySchema = z35.string().trim().min(1).max(MAX_ALIAS_LENGTH).refine(
18057
18443
  (value) => !/[\p{Cc}\uD800-\uDFFF]/u.test(value),
18058
18444
  "Identity must contain only printable non-control Unicode"
18059
18445
  );
18060
18446
  var PalAliasSchema = PrintableIdentitySchema;
18061
18447
  var PalTargetSchema = PrintableIdentitySchema;
18062
- var BoundedTextSchema = z33.string().refine(
18448
+ var BoundedTextSchema = z35.string().refine(
18063
18449
  (value) => Buffer.byteLength(value, "utf8") <= MAX_MESSAGE_BYTES,
18064
18450
  `Text must not exceed ${MAX_MESSAGE_BYTES} UTF-8 bytes`
18065
18451
  );
18066
18452
  var NonEmptyBoundedTextSchema = BoundedTextSchema.refine((value) => value.length > 0, "Text must not be empty");
18067
- var CompletionDetailSchema = z33.string().trim().min(1).refine(
18453
+ var CompletionDetailSchema = z35.string().trim().min(1).refine(
18068
18454
  (value) => Buffer.byteLength(value, "utf8") <= MAX_COMPLETION_DETAIL_BYTES,
18069
18455
  `Completion evidence must not exceed ${MAX_COMPLETION_DETAIL_BYTES} UTF-8 bytes`
18070
18456
  );
18071
- var PalPresenceSchema = z33.object({
18457
+ var PalPresenceSchema = z35.object({
18072
18458
  version: VersionSchema,
18073
18459
  id: UuidSchema,
18074
18460
  alias: PalAliasSchema,
18075
- projectPath: z33.string().min(1).refine(
18461
+ projectPath: z35.string().min(1).refine(
18076
18462
  (value) => Buffer.byteLength(value, "utf8") <= MAX_PROJECT_PATH_BYTES,
18077
18463
  `Project path must not exceed ${MAX_PROJECT_PATH_BYTES} UTF-8 bytes`
18078
18464
  ),
18079
- connectedAt: z33.iso.datetime(),
18080
- lastSeenAt: z33.iso.datetime()
18465
+ connectedAt: z35.iso.datetime(),
18466
+ lastSeenAt: z35.iso.datetime()
18081
18467
  }).strict();
18082
- var PalPresenceListSchema = z33.array(PalPresenceSchema).max(MAX_ACTIVE_PALS);
18083
- var PalTaskMessageSchema = z33.object({
18468
+ var PalPresenceListSchema = z35.array(PalPresenceSchema).max(MAX_ACTIVE_PALS);
18469
+ var PalTaskMessageSchema = z35.object({
18084
18470
  version: VersionSchema,
18085
- type: z33.literal("task"),
18471
+ type: z35.literal("task"),
18086
18472
  messageId: UuidSchema,
18087
18473
  target: PalTargetSchema,
18088
18474
  goal: NonEmptyBoundedTextSchema
18089
18475
  }).strict();
18090
- var PalTaskEventSchema = z33.object({
18476
+ var PalTaskEventSchema = z35.object({
18091
18477
  version: VersionSchema,
18092
- type: z33.literal("task-event"),
18478
+ type: z35.literal("task-event"),
18093
18479
  messageId: UuidSchema,
18094
18480
  taskId: UuidSchema,
18095
18481
  senderId: UuidSchema,
18096
18482
  recipientId: UuidSchema,
18097
- status: z33.enum(["accepted", "started", "progress", "completed", "failed", "cancelled"]),
18483
+ status: z35.enum(["accepted", "started", "progress", "completed", "failed", "cancelled"]),
18098
18484
  detail: BoundedTextSchema.optional()
18099
18485
  }).strict();
18100
- var DeliveryReceiptSchema = z33.object({
18486
+ var DeliveryReceiptSchema = z35.object({
18101
18487
  version: VersionSchema,
18102
- type: z33.literal("delivery-receipt"),
18488
+ type: z35.literal("delivery-receipt"),
18103
18489
  messageId: UuidSchema,
18104
- status: z33.enum(["delivered", "duplicate", "rejected"]),
18105
- recipientIds: z33.array(UuidSchema).max(MAX_PARTICIPANTS),
18490
+ status: z35.enum(["delivered", "duplicate", "rejected"]),
18491
+ recipientIds: z35.array(UuidSchema).max(MAX_PARTICIPANTS),
18106
18492
  error: BoundedTextSchema.optional()
18107
18493
  }).strict();
18108
- var CoWorkParticipantSchema = z33.object({
18494
+ var CoWorkParticipantSchema = z35.object({
18109
18495
  palId: UuidSchema,
18110
- required: z33.boolean()
18496
+ required: z35.boolean()
18111
18497
  }).strict();
18112
- var CoWorkProposalParticipantsSchema = z33.array(CoWorkParticipantSchema).min(2).max(MAX_PARTICIPANTS).superRefine((participants, context) => {
18498
+ var CoWorkProposalParticipantsSchema = z35.array(CoWorkParticipantSchema).min(2).max(MAX_PARTICIPANTS).superRefine((participants, context) => {
18113
18499
  const seen = /* @__PURE__ */ new Set();
18114
18500
  for (const [index, participant] of participants.entries()) {
18115
18501
  const id = participant.palId.toLowerCase();
@@ -18119,19 +18505,19 @@ var CoWorkProposalParticipantsSchema = z33.array(CoWorkParticipantSchema).min(2)
18119
18505
  seen.add(id);
18120
18506
  }
18121
18507
  });
18122
- var CoWorkPlanTaskSchema = z33.object({
18123
- id: z33.string().min(1).max(128),
18508
+ var CoWorkPlanTaskSchema = z35.object({
18509
+ id: z35.string().min(1).max(128),
18124
18510
  assigneeId: UuidSchema,
18125
18511
  description: NonEmptyBoundedTextSchema,
18126
- dependsOn: z33.array(z33.string().min(1).max(128)).max(MAX_PLAN_TASKS)
18512
+ dependsOn: z35.array(z35.string().min(1).max(128)).max(MAX_PLAN_TASKS)
18127
18513
  }).strict();
18128
- var CoWorkPlanSchema = z33.object({
18514
+ var CoWorkPlanSchema = z35.object({
18129
18515
  version: VersionSchema,
18130
18516
  coWorkId: UuidSchema,
18131
- epoch: z33.number().int().positive(),
18517
+ epoch: z35.number().int().positive(),
18132
18518
  goal: NonEmptyBoundedTextSchema,
18133
- participants: z33.array(CoWorkParticipantSchema).min(1).max(MAX_PARTICIPANTS),
18134
- tasks: z33.array(CoWorkPlanTaskSchema).min(1).max(MAX_PLAN_TASKS)
18519
+ participants: z35.array(CoWorkParticipantSchema).min(1).max(MAX_PARTICIPANTS),
18520
+ tasks: z35.array(CoWorkPlanTaskSchema).min(1).max(MAX_PLAN_TASKS)
18135
18521
  }).strict().superRefine((plan, context) => {
18136
18522
  if (Buffer.byteLength(JSON.stringify(plan), "utf8") > MAX_PLAN_ENCODED_BYTES) {
18137
18523
  context.addIssue({ code: "custom", message: `Encoded plan must not exceed ${MAX_PLAN_ENCODED_BYTES} UTF-8 bytes` });
@@ -18181,7 +18567,7 @@ var CoWorkPlanSchema = z33.object({
18181
18567
  }
18182
18568
  }
18183
18569
  });
18184
- var CoWorkPhaseSchema = z33.enum([
18570
+ var CoWorkPhaseSchema = z35.enum([
18185
18571
  "proposed",
18186
18572
  "planning",
18187
18573
  "prepared",
@@ -18191,30 +18577,30 @@ var CoWorkPhaseSchema = z33.enum([
18191
18577
  "failed",
18192
18578
  "cancelled"
18193
18579
  ]);
18194
- var IntegrationResultSchema = z33.object({
18195
- passed: z33.boolean(),
18580
+ var IntegrationResultSchema = z35.object({
18581
+ passed: z35.boolean(),
18196
18582
  evidence: CompletionDetailSchema
18197
18583
  }).strict();
18198
- var CoWorkCompletionAssertionSchema = z33.object({
18584
+ var CoWorkCompletionAssertionSchema = z35.object({
18199
18585
  participantId: UuidSchema,
18200
- passed: z33.boolean(),
18586
+ passed: z35.boolean(),
18201
18587
  detail: CompletionDetailSchema.optional()
18202
18588
  }).strict();
18203
- var CoWorkSnapshotSchema = z33.object({
18589
+ var CoWorkSnapshotSchema = z35.object({
18204
18590
  version: VersionSchema,
18205
18591
  coWorkId: UuidSchema,
18206
- epoch: z33.number().int().positive(),
18592
+ epoch: z35.number().int().positive(),
18207
18593
  phase: CoWorkPhaseSchema,
18208
18594
  goal: NonEmptyBoundedTextSchema,
18209
- participants: z33.array(CoWorkParticipantSchema).min(1).max(MAX_PARTICIPANTS),
18595
+ participants: z35.array(CoWorkParticipantSchema).min(1).max(MAX_PARTICIPANTS),
18210
18596
  integrationOwnerId: UuidSchema,
18211
- acceptedParticipantIds: z33.array(UuidSchema).max(MAX_PARTICIPANTS),
18212
- planHash: z33.string().regex(/^[a-f0-9]{64}$/).nullable(),
18597
+ acceptedParticipantIds: z35.array(UuidSchema).max(MAX_PARTICIPANTS),
18598
+ planHash: z35.string().regex(/^[a-f0-9]{64}$/).nullable(),
18213
18599
  plan: CoWorkPlanSchema.nullable(),
18214
- planAcceptedParticipantIds: z33.array(UuidSchema).max(MAX_PARTICIPANTS),
18215
- readyParticipantIds: z33.array(UuidSchema).max(MAX_PARTICIPANTS),
18216
- completedParticipantIds: z33.array(UuidSchema).max(MAX_PARTICIPANTS),
18217
- completionAssertions: z33.array(CoWorkCompletionAssertionSchema).max(MAX_PARTICIPANTS),
18600
+ planAcceptedParticipantIds: z35.array(UuidSchema).max(MAX_PARTICIPANTS),
18601
+ readyParticipantIds: z35.array(UuidSchema).max(MAX_PARTICIPANTS),
18602
+ completedParticipantIds: z35.array(UuidSchema).max(MAX_PARTICIPANTS),
18603
+ completionAssertions: z35.array(CoWorkCompletionAssertionSchema).max(MAX_PARTICIPANTS),
18218
18604
  integration: IntegrationResultSchema.nullable()
18219
18605
  }).strict().superRefine((snapshot4, context) => {
18220
18606
  if (Buffer.byteLength(JSON.stringify(snapshot4), "utf8") > MAX_SNAPSHOT_ENCODED_BYTES) {
@@ -18245,87 +18631,87 @@ var RequestBase = {
18245
18631
  version: VersionSchema,
18246
18632
  requestId: UuidSchema
18247
18633
  };
18248
- var RegisterRequestSchema = z33.object({
18634
+ var RegisterRequestSchema = z35.object({
18249
18635
  ...RequestBase,
18250
- type: z33.literal("register"),
18251
- authToken: z33.string().regex(/^[a-f0-9]{64}$/).optional(),
18636
+ type: z35.literal("register"),
18637
+ authToken: z35.string().regex(/^[a-f0-9]{64}$/).optional(),
18252
18638
  presence: PalPresenceSchema
18253
18639
  }).strict();
18254
- var HeartbeatRequestSchema = z33.object({ ...RequestBase, type: z33.literal("heartbeat") }).strict();
18255
- var ListRequestSchema = z33.object({ ...RequestBase, type: z33.literal("list") }).strict();
18256
- var DisconnectRequestSchema = z33.object({ ...RequestBase, type: z33.literal("disconnect") }).strict();
18257
- var ChatRequestSchema = z33.object({
18640
+ var HeartbeatRequestSchema = z35.object({ ...RequestBase, type: z35.literal("heartbeat") }).strict();
18641
+ var ListRequestSchema = z35.object({ ...RequestBase, type: z35.literal("list") }).strict();
18642
+ var DisconnectRequestSchema = z35.object({ ...RequestBase, type: z35.literal("disconnect") }).strict();
18643
+ var ChatRequestSchema = z35.object({
18258
18644
  ...RequestBase,
18259
- type: z33.literal("chat"),
18645
+ type: z35.literal("chat"),
18260
18646
  messageId: UuidSchema,
18261
18647
  target: PalTargetSchema,
18262
18648
  message: NonEmptyBoundedTextSchema
18263
18649
  }).strict();
18264
- var TaskRequestSchema = z33.object({
18650
+ var TaskRequestSchema = z35.object({
18265
18651
  ...RequestBase,
18266
- type: z33.literal("task"),
18652
+ type: z35.literal("task"),
18267
18653
  messageId: UuidSchema,
18268
18654
  target: PalTargetSchema,
18269
18655
  goal: NonEmptyBoundedTextSchema
18270
18656
  }).strict();
18271
- var CoWorkProposeRequestSchema = z33.object({
18657
+ var CoWorkProposeRequestSchema = z35.object({
18272
18658
  ...RequestBase,
18273
- type: z33.literal("cowork-propose"),
18659
+ type: z35.literal("cowork-propose"),
18274
18660
  coWorkId: UuidSchema,
18275
18661
  goal: NonEmptyBoundedTextSchema,
18276
18662
  participants: CoWorkProposalParticipantsSchema
18277
18663
  }).strict();
18278
- var CoWorkAcceptanceRequestSchema = z33.object({
18664
+ var CoWorkAcceptanceRequestSchema = z35.object({
18279
18665
  ...RequestBase,
18280
- type: z33.literal("cowork-accept"),
18666
+ type: z35.literal("cowork-accept"),
18281
18667
  coWorkId: UuidSchema,
18282
- epoch: z33.number().int().positive()
18668
+ epoch: z35.number().int().positive()
18283
18669
  }).strict();
18284
- var CoWorkPlanRequestSchema = z33.object({ ...RequestBase, type: z33.literal("cowork-plan"), plan: CoWorkPlanSchema }).strict();
18285
- var CoWorkPlanAcceptanceRequestSchema = z33.object({
18670
+ var CoWorkPlanRequestSchema = z35.object({ ...RequestBase, type: z35.literal("cowork-plan"), plan: CoWorkPlanSchema }).strict();
18671
+ var CoWorkPlanAcceptanceRequestSchema = z35.object({
18286
18672
  ...RequestBase,
18287
- type: z33.literal("cowork-plan-accept"),
18673
+ type: z35.literal("cowork-plan-accept"),
18288
18674
  coWorkId: UuidSchema,
18289
- epoch: z33.number().int().positive(),
18290
- planHash: z33.string().regex(/^[a-f0-9]{64}$/)
18675
+ epoch: z35.number().int().positive(),
18676
+ planHash: z35.string().regex(/^[a-f0-9]{64}$/)
18291
18677
  }).strict();
18292
- var CoWorkReadyRequestSchema = z33.object({
18678
+ var CoWorkReadyRequestSchema = z35.object({
18293
18679
  ...RequestBase,
18294
- type: z33.literal("cowork-ready"),
18680
+ type: z35.literal("cowork-ready"),
18295
18681
  coWorkId: UuidSchema,
18296
- epoch: z33.number().int().positive(),
18297
- planHash: z33.string().regex(/^[a-f0-9]{64}$/)
18682
+ epoch: z35.number().int().positive(),
18683
+ planHash: z35.string().regex(/^[a-f0-9]{64}$/)
18298
18684
  }).strict();
18299
- var CoWorkCompleteRequestSchema = z33.object({
18685
+ var CoWorkCompleteRequestSchema = z35.object({
18300
18686
  ...RequestBase,
18301
- type: z33.literal("cowork-complete"),
18687
+ type: z35.literal("cowork-complete"),
18302
18688
  coWorkId: UuidSchema,
18303
- epoch: z33.number().int().positive(),
18304
- planHash: z33.string().regex(/^[a-f0-9]{64}$/),
18305
- passed: z33.boolean(),
18689
+ epoch: z35.number().int().positive(),
18690
+ planHash: z35.string().regex(/^[a-f0-9]{64}$/),
18691
+ passed: z35.boolean(),
18306
18692
  detail: CompletionDetailSchema.optional()
18307
18693
  }).strict();
18308
- var CoWorkIntegrationRequestSchema = z33.object({
18694
+ var CoWorkIntegrationRequestSchema = z35.object({
18309
18695
  ...RequestBase,
18310
- type: z33.literal("cowork-integration"),
18696
+ type: z35.literal("cowork-integration"),
18311
18697
  coWorkId: UuidSchema,
18312
- epoch: z33.number().int().positive(),
18313
- planHash: z33.string().regex(/^[a-f0-9]{64}$/),
18314
- passed: z33.boolean(),
18698
+ epoch: z35.number().int().positive(),
18699
+ planHash: z35.string().regex(/^[a-f0-9]{64}$/),
18700
+ passed: z35.boolean(),
18315
18701
  evidence: CompletionDetailSchema
18316
18702
  }).strict();
18317
- var CoWorkGetRequestSchema = z33.object({
18703
+ var CoWorkGetRequestSchema = z35.object({
18318
18704
  ...RequestBase,
18319
- type: z33.literal("cowork-get"),
18705
+ type: z35.literal("cowork-get"),
18320
18706
  coWorkId: UuidSchema
18321
18707
  }).strict();
18322
- var CoWorkCancelRequestSchema = z33.object({
18708
+ var CoWorkCancelRequestSchema = z35.object({
18323
18709
  ...RequestBase,
18324
- type: z33.literal("cowork-cancel"),
18710
+ type: z35.literal("cowork-cancel"),
18325
18711
  coWorkId: UuidSchema,
18326
18712
  reason: NonEmptyBoundedTextSchema
18327
18713
  }).strict();
18328
- var BrokerRequestSchema = z33.discriminatedUnion("type", [
18714
+ var BrokerRequestSchema = z35.discriminatedUnion("type", [
18329
18715
  RegisterRequestSchema,
18330
18716
  HeartbeatRequestSchema,
18331
18717
  ListRequestSchema,
@@ -18342,49 +18728,49 @@ var BrokerRequestSchema = z33.discriminatedUnion("type", [
18342
18728
  CoWorkGetRequestSchema,
18343
18729
  CoWorkCancelRequestSchema
18344
18730
  ]);
18345
- var BrokerOkResponseSchema = z33.object({
18731
+ var BrokerOkResponseSchema = z35.object({
18346
18732
  version: VersionSchema,
18347
- type: z33.literal("ok"),
18733
+ type: z35.literal("ok"),
18348
18734
  requestId: UuidSchema,
18349
- data: z33.unknown()
18735
+ data: z35.unknown()
18350
18736
  }).strict();
18351
- var BrokerErrorResponseSchema = z33.object({
18737
+ var BrokerErrorResponseSchema = z35.object({
18352
18738
  version: VersionSchema,
18353
- type: z33.literal("error"),
18739
+ type: z35.literal("error"),
18354
18740
  requestId: UuidSchema,
18355
- code: z33.enum(["invalid-request", "authentication-failed", "not-found", "ambiguous-target", "alias-conflict", "invalid-transition", "stale-epoch", "duplicate"]),
18741
+ code: z35.enum(["invalid-request", "authentication-failed", "not-found", "ambiguous-target", "alias-conflict", "invalid-transition", "stale-epoch", "duplicate"]),
18356
18742
  message: BoundedTextSchema
18357
18743
  }).strict();
18358
- var ChatEventSchema = z33.object({
18744
+ var ChatEventSchema = z35.object({
18359
18745
  version: VersionSchema,
18360
- type: z33.literal("chat-event"),
18746
+ type: z35.literal("chat-event"),
18361
18747
  messageId: UuidSchema,
18362
18748
  senderId: UuidSchema,
18363
18749
  recipientId: UuidSchema,
18364
18750
  message: NonEmptyBoundedTextSchema
18365
18751
  }).strict();
18366
- var CoWorkEventSchema = z33.object({
18752
+ var CoWorkEventSchema = z35.object({
18367
18753
  version: VersionSchema,
18368
- type: z33.literal("cowork-event"),
18369
- action: z33.enum(["PROPOSE", "PLAN", "START", "END", "FAIL", "CANCEL"]),
18754
+ type: z35.literal("cowork-event"),
18755
+ action: z35.enum(["PROPOSE", "PLAN", "START", "END", "FAIL", "CANCEL"]),
18370
18756
  actorId: UuidSchema,
18371
18757
  coWorkId: UuidSchema,
18372
- epoch: z33.number().int().positive(),
18373
- planHash: z33.string().regex(/^[a-f0-9]{64}$/).nullable(),
18758
+ epoch: z35.number().int().positive(),
18759
+ planHash: z35.string().regex(/^[a-f0-9]{64}$/).nullable(),
18374
18760
  snapshot: CoWorkSnapshotSchema
18375
18761
  }).strict();
18376
- var BrokerEventSchema = z33.union([ChatEventSchema, PalTaskEventSchema, DeliveryReceiptSchema, CoWorkEventSchema]);
18377
- var BrokerEventResponseSchema = z33.object({
18762
+ var BrokerEventSchema = z35.union([ChatEventSchema, PalTaskEventSchema, DeliveryReceiptSchema, CoWorkEventSchema]);
18763
+ var BrokerEventResponseSchema = z35.object({
18378
18764
  version: VersionSchema,
18379
- type: z33.literal("event"),
18765
+ type: z35.literal("event"),
18380
18766
  event: BrokerEventSchema
18381
18767
  }).strict();
18382
- var BrokerResponseSchema = z33.discriminatedUnion("type", [
18768
+ var BrokerResponseSchema = z35.discriminatedUnion("type", [
18383
18769
  BrokerOkResponseSchema,
18384
18770
  BrokerErrorResponseSchema,
18385
18771
  BrokerEventResponseSchema
18386
18772
  ]);
18387
- var ControlFrameSchema = z33.string().refine(
18773
+ var ControlFrameSchema = z35.string().refine(
18388
18774
  (frame) => Buffer.byteLength(frame, "utf8") <= MAX_CONTROL_FRAME_BYTES,
18389
18775
  `Control frame must not exceed ${MAX_CONTROL_FRAME_BYTES} UTF-8 bytes`
18390
18776
  );
@@ -18419,6 +18805,8 @@ var MVP_COMMANDS = [
18419
18805
  "goal",
18420
18806
  "evolve",
18421
18807
  "mcp",
18808
+ "commit",
18809
+ "review",
18422
18810
  "ide",
18423
18811
  "memory",
18424
18812
  "remember",
@@ -18469,6 +18857,14 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
18469
18857
  if (name === "evolve") {
18470
18858
  return { name, args };
18471
18859
  }
18860
+ if (name === "commit") {
18861
+ const hint = args.join(" ").trim();
18862
+ return hint.length === 0 ? { name } : { name, hint };
18863
+ }
18864
+ if (name === "review") {
18865
+ const focus = args.join(" ").trim();
18866
+ return focus.length === 0 ? { name } : { name, focus };
18867
+ }
18472
18868
  if (name === "loop") {
18473
18869
  const goal = args.join(" ").trim();
18474
18870
  if (!goal) return { name: "invalid", command: name, message: "Use /loop <goal>." };
@@ -18676,6 +19072,8 @@ var HELP = [
18676
19072
  "/compact /clear /help /exit",
18677
19073
  "/loop <goal> run a verified autonomous loop",
18678
19074
  "/goal <objective> run a goal pipeline with adversarial verification",
19075
+ "/commit [hint] draft a commit message for staged changes and commit",
19076
+ "/review [focus] review uncommitted changes before committing",
18679
19077
  "/evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>> self-improvement loop",
18680
19078
  "/mcp [status|tools|reconnect|enable|disable] manage MCP servers",
18681
19079
  "/ide show VS Code connection and cursor/selection",
@@ -19016,6 +19414,10 @@ var FlavorSession = class {
19016
19414
  for await (const event of this.#services.runLoop(command.goal, signal)) this.#services.output(event);
19017
19415
  } else if (command.name === "goal") {
19018
19416
  for await (const event of this.#services.runGoal(command.goal, signal)) this.#services.output(event);
19417
+ } else if (command.name === "commit") {
19418
+ this.#notice(await required(this.#services.gitCommit, "commit")(command.hint, signal));
19419
+ } else if (command.name === "review") {
19420
+ this.#notice(await required(this.#services.gitReview, "review")(command.focus, signal));
19019
19421
  } else if (command.name === "mcp") {
19020
19422
  this.#notice(await this.#services.mcp(command, signal));
19021
19423
  } else if (command.name === "pals") {
@@ -19285,7 +19687,7 @@ var LANG_LABELS = /* @__PURE__ */ new Map([
19285
19687
  ]);
19286
19688
 
19287
19689
  // src/hallucination/confidence.ts
19288
- import { z as z34 } from "zod";
19690
+ import { z as z36 } from "zod";
19289
19691
 
19290
19692
  // src/hallucination/types.ts
19291
19693
  var DEFAULT_SLIDING_WINDOW_SIZE = 20;
@@ -19298,12 +19700,12 @@ var DEFAULT_EVALUATION_TIMEOUT_MS = 2e3;
19298
19700
  var MAX_QUERY_CHARS = 5e3;
19299
19701
  var MAX_OUTPUT_CHARS = 1e4;
19300
19702
  var MAX_EVIDENCE_CHARS = 6e3;
19301
- var ConfidenceSchema = z34.object({
19302
- taskAlignment: z34.number(),
19303
- evidenceGrounding: z34.number(),
19304
- processReliability: z34.number(),
19305
- reason: z34.string(),
19306
- unsupportedClaims: z34.array(z34.string()).max(3)
19703
+ var ConfidenceSchema = z36.object({
19704
+ taskAlignment: z36.number(),
19705
+ evidenceGrounding: z36.number(),
19706
+ processReliability: z36.number(),
19707
+ reason: z36.string(),
19708
+ unsupportedClaims: z36.array(z36.string()).max(3)
19307
19709
  });
19308
19710
  var PROMPT = `You are a fast, evidence-aware task-completion verifier.
19309
19711
 
@@ -21396,15 +21798,15 @@ function workspacePath(root, input) {
21396
21798
  }
21397
21799
 
21398
21800
  // src/tools/terminal.ts
21399
- import { z as z35 } from "zod";
21801
+ import { z as z37 } from "zod";
21400
21802
  import { resolve as resolve29 } from "path";
21401
- var Id = z35.string().regex(/^term-[a-f0-9-]+$/);
21402
- var OpenInput = z35.object({ cwd: z35.string().optional(), shell: z35.string().optional(), args: z35.array(z35.string()).optional(), columns: z35.coerce.number().int().min(10).max(500).optional(), rows: z35.coerce.number().int().min(2).max(300).optional() });
21403
- var OptionalBoolean = z35.union([z35.boolean(), z35.string().refine((value) => value === "true" || value === "false")]);
21404
- var WriteInput2 = z35.object({ id: Id, data: z35.string().max(1e5), enter: OptionalBoolean.optional() });
21405
- var ReadInput3 = z35.object({ id: Id, cursor: z35.coerce.number().int().min(0).optional() });
21406
- var ResizeInput = z35.object({ id: Id, columns: z35.coerce.number().int().min(10).max(500), rows: z35.coerce.number().int().min(2).max(300) });
21407
- var CloseInput = z35.object({ id: Id });
21803
+ var Id = z37.string().regex(/^term-[a-f0-9-]+$/);
21804
+ var OpenInput = z37.object({ cwd: z37.string().optional(), shell: z37.string().optional(), args: z37.array(z37.string()).optional(), columns: z37.coerce.number().int().min(10).max(500).optional(), rows: z37.coerce.number().int().min(2).max(300).optional() });
21805
+ var OptionalBoolean = z37.union([z37.boolean(), z37.string().refine((value) => value === "true" || value === "false")]);
21806
+ var WriteInput2 = z37.object({ id: Id, data: z37.string().max(1e5), enter: OptionalBoolean.optional() });
21807
+ var ReadInput3 = z37.object({ id: Id, cursor: z37.coerce.number().int().min(0).optional() });
21808
+ var ResizeInput = z37.object({ id: Id, columns: z37.coerce.number().int().min(10).max(500), rows: z37.coerce.number().int().min(2).max(300) });
21809
+ var CloseInput = z37.object({ id: Id });
21408
21810
  function createTerminalTools(terminals, workspace = process.cwd()) {
21409
21811
  const tools = [
21410
21812
  {
@@ -21480,7 +21882,7 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
21480
21882
  {
21481
21883
  name: "TerminalList",
21482
21884
  description: "List persistent terminals owned by this agent",
21483
- inputSchema: z35.object({}),
21885
+ inputSchema: z37.object({}),
21484
21886
  paths: () => [],
21485
21887
  execute: async (_input, _signal, context) => terminals.list(owner(context))
21486
21888
  }
@@ -22056,7 +22458,7 @@ var PalClient = class {
22056
22458
  };
22057
22459
 
22058
22460
  // src/pals/tools.ts
22059
- import { z as z36 } from "zod";
22461
+ import { z as z38 } from "zod";
22060
22462
  var DEFAULT_SHARE_BUDGET_BYTES = 128 * 1024;
22061
22463
  var CollaborationShareGuard = class {
22062
22464
  redact;
@@ -22077,25 +22479,25 @@ var CollaborationShareGuard = class {
22077
22479
  return redacted;
22078
22480
  }
22079
22481
  };
22080
- var Uuid = z36.uuid();
22081
- var Hash = z36.string().regex(/^[a-f0-9]{64}$/);
22482
+ var Uuid = z38.uuid();
22483
+ var Hash = z38.string().regex(/^[a-f0-9]{64}$/);
22082
22484
  var Target = PalTargetSchema;
22083
- var BoundedText = z36.string().trim().min(1).refine(
22485
+ var BoundedText = z38.string().trim().min(1).refine(
22084
22486
  (value) => Buffer.byteLength(value, "utf8") <= MAX_MESSAGE_BYTES,
22085
22487
  `Text must not exceed ${MAX_MESSAGE_BYTES} UTF-8 bytes`
22086
22488
  );
22087
- var CompletionEvidence = z36.string().trim().min(1).refine(
22489
+ var CompletionEvidence = z38.string().trim().min(1).refine(
22088
22490
  (value) => Buffer.byteLength(value, "utf8") <= MAX_COMPLETION_DETAIL_BYTES,
22089
22491
  `Completion evidence must not exceed ${MAX_COMPLETION_DETAIL_BYTES} UTF-8 bytes`
22090
22492
  );
22091
- var EmptyInput = z36.object({}).strict();
22092
- var StateInput = z36.object({ coWorkId: Uuid }).strict();
22093
- var TokenInput = z36.object({ coWorkId: Uuid, epoch: z36.number().int().positive(), planHash: Hash }).strict();
22094
- var SendInput = z36.object({ target: Target, message: BoundedText, coWorkId: Uuid.optional() }).strict();
22095
- var PlanInput = z36.object({ plan: CoWorkPlanSchema }).strict();
22096
- var ProgressInput = z36.object({ coWorkId: Uuid, target: Target, detail: BoundedText }).strict();
22097
- var CompleteInput = TokenInput.extend({ passed: z36.boolean(), detail: CompletionEvidence }).strict();
22098
- var IntegrateInput = TokenInput.extend({ passed: z36.boolean(), evidence: CompletionEvidence }).strict();
22493
+ var EmptyInput = z38.object({}).strict();
22494
+ var StateInput = z38.object({ coWorkId: Uuid }).strict();
22495
+ var TokenInput = z38.object({ coWorkId: Uuid, epoch: z38.number().int().positive(), planHash: Hash }).strict();
22496
+ var SendInput = z38.object({ target: Target, message: BoundedText, coWorkId: Uuid.optional() }).strict();
22497
+ var PlanInput = z38.object({ plan: CoWorkPlanSchema }).strict();
22498
+ var ProgressInput = z38.object({ coWorkId: Uuid, target: Target, detail: BoundedText }).strict();
22499
+ var CompleteInput = TokenInput.extend({ passed: z38.boolean(), detail: CompletionEvidence }).strict();
22500
+ var IntegrateInput = TokenInput.extend({ passed: z38.boolean(), evidence: CompletionEvidence }).strict();
22099
22501
  var sharedBytes = (...values) => values.reduce((total, value) => total + Buffer.byteLength(value, "utf8"), 0);
22100
22502
  function resolvePresence(presences, target) {
22101
22503
  const normalized = normalizePalIdentity(target);
@@ -22448,6 +22850,7 @@ async function createProductionRuntime(options) {
22448
22850
  createApplyPatchTool(workspace, fileMutationOptions),
22449
22851
  createGlobTool(workspace),
22450
22852
  createGrepTool(workspace),
22853
+ createGitHistoryTool(workspace),
22451
22854
  createShellTool(workspace, {
22452
22855
  jobs,
22453
22856
  ...executionEnvironment === void 0 ? {} : { executionEnvironment }
@@ -22824,7 +23227,7 @@ async function createProductionRuntime(options) {
22824
23227
  })) tools.push(tool);
22825
23228
  const taskTool = {
22826
23229
  name: "Task",
22827
- description: "Validate a task graph and execute its nodes with isolated child agents",
23230
+ 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.",
22828
23231
  inputSchema: TaskGraphSchema,
22829
23232
  paths: () => [],
22830
23233
  execute: async (input, signal) => {
@@ -23512,6 +23915,19 @@ async function createProductionRuntime(options) {
23512
23915
  hooksStatus: () => HOOK_EVENT_NAMES.map((name) => ({ name, pluginHandlers: pluginHooks.filter((item) => item === name).length })),
23513
23916
  tasks: () => ({ plan: taskPlan, graph: taskGraph, states: taskStates, results: taskResults }),
23514
23917
  evolve: (args) => evolveService.handleCommand(args),
23918
+ gitCommit: (hint, signal) => runGitCommit({
23919
+ workspace,
23920
+ registry,
23921
+ questions,
23922
+ modelId: () => harness.subagentModelId,
23923
+ notify: (message2) => emitOutput({ type: "notice", message: message2 })
23924
+ }, hint, signal),
23925
+ gitReview: (focus, signal) => runGitReview({
23926
+ workspace,
23927
+ registry,
23928
+ modelId: () => harness.subagentModelId,
23929
+ notify: (message2) => emitOutput({ type: "notice", message: message2 })
23930
+ }, focus, signal),
23515
23931
  audit: async (toolFilter) => {
23516
23932
  try {
23517
23933
  const raw = await readFile25(auditLogger.path, "utf8");
@@ -23599,7 +24015,11 @@ ${summary2}`;
23599
24015
  },
23600
24016
  ide: () => ide.status(),
23601
24017
  ideContext: () => ide.editorContext(),
23602
- checkpoint: (label) => sessionHistory.checkpoint(label, harness.main.context.snapshot()),
24018
+ checkpoint: async (label) => {
24019
+ const marker = await gitMarker(workspace);
24020
+ const tagged = marker === void 0 ? label : label === void 0 ? `git: ${marker}` : `${label} (git: ${marker})`;
24021
+ return sessionHistory.checkpoint(tagged, harness.main.context.snapshot());
24022
+ },
23603
24023
  tree: () => sessionHistory.tree(),
23604
24024
  rewind: async (nodeId) => {
23605
24025
  await sessionHistory.rewind(nodeId, harness.main.context.snapshot());
@@ -24031,6 +24451,70 @@ async function* persistEach(source, persist) {
24031
24451
  await persist();
24032
24452
  }
24033
24453
  }
24454
+ async function runGitCommit(deps, hint, signal) {
24455
+ if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
24456
+ let summary2 = await changeSummary(deps.workspace);
24457
+ if (summary2.statusLines.length === 0) return "Working tree clean \u2014 nothing to commit.";
24458
+ if (summary2.stagedFiles.length === 0) {
24459
+ if (deps.questions === void 0) throw new Error("Nothing is staged. Run `git add` on the files you want to commit first.");
24460
+ const stageAnswers = await deps.questions.ask([{
24461
+ header: "Staging",
24462
+ question: `Nothing is staged, but ${summary2.statusLines.length} change(s) exist. Stage everything before committing?`,
24463
+ options: [
24464
+ { label: "Stage all", description: "Run git add -A, then generate the commit message." },
24465
+ { label: "Cancel", description: "Abort /commit and leave the working tree as-is." }
24466
+ ]
24467
+ }], signal);
24468
+ if (stageAnswers[0] !== "Stage all") return "/commit cancelled \u2014 nothing was staged.";
24469
+ await stageAll(deps.workspace);
24470
+ summary2 = await changeSummary(deps.workspace);
24471
+ if (summary2.stagedFiles.length === 0) return "Nothing ended up staged \u2014 /commit aborted.";
24472
+ }
24473
+ const staged = await stagedDiff(deps.workspace);
24474
+ signal.throwIfAborted();
24475
+ deps.notify?.("Generating commit message\u2026");
24476
+ let commitMessage;
24477
+ try {
24478
+ commitMessage = await suggestCommitMessage(
24479
+ { registry: deps.registry, modelId: deps.modelId },
24480
+ { stat: staged.stat, diff: staged.diff, ...hint === void 0 ? {} : { hint } },
24481
+ signal
24482
+ );
24483
+ } catch (error) {
24484
+ signal.throwIfAborted();
24485
+ const scope2 = summary2.stagedFiles.length === 1 ? summary2.stagedFiles[0] ?? "1 file" : `${summary2.stagedFiles.length} files`;
24486
+ commitMessage = `chore: update ${scope2}`;
24487
+ deps.notify?.(`Commit message generation failed (${message(error)}); using a fallback message.`);
24488
+ }
24489
+ deps.notify?.(`Proposed commit message:
24490
+ ${commitMessage}`);
24491
+ if (deps.questions !== void 0) {
24492
+ const confirmAnswers = await deps.questions.ask([{
24493
+ header: "Commit",
24494
+ question: `Commit ${summary2.stagedFiles.length} staged file(s) on ${summary2.branch} with the message above?`,
24495
+ options: [
24496
+ { label: "Commit", description: "Run git commit with the proposed message." },
24497
+ { label: "Cancel", description: "Abort; the staged changes stay as-is." }
24498
+ ]
24499
+ }], signal);
24500
+ if (confirmAnswers[0] !== "Commit") return "/commit cancelled \u2014 staged changes left untouched.";
24501
+ }
24502
+ const result = await commit(deps.workspace, commitMessage);
24503
+ return `Committed: ${result}`;
24504
+ }
24505
+ async function runGitReview(deps, focus, signal) {
24506
+ if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /review needs git.";
24507
+ const changes = await uncommittedDiff(deps.workspace);
24508
+ if (changes.diff.trim() === "" && changes.untracked.length === 0) return "No uncommitted changes to review.";
24509
+ signal.throwIfAborted();
24510
+ deps.notify?.("Reviewing uncommitted changes\u2026");
24511
+ const report = await reviewDiff(
24512
+ { registry: deps.registry, modelId: deps.modelId },
24513
+ { stat: changes.stat, diff: changes.diff, untracked: changes.untracked, ...focus === void 0 ? {} : { focus } },
24514
+ signal
24515
+ );
24516
+ return formatReviewReport(report);
24517
+ }
24034
24518
  async function runChild(harness, skills, task, attempt, signal, parentContext) {
24035
24519
  return harness.runSubagent(task, async (child, childSignal) => {
24036
24520
  const skill = await skills.match(task.description);
@@ -24043,6 +24527,7 @@ ${await skills.loadBody(skill)}`;
24043
24527
  `Complete task ${task.id}: ${task.description}`,
24044
24528
  `Expected outputs: ${task.expectedOutputs.join("; ")}`,
24045
24529
  `Verification: ${task.verification.join("; ")}`,
24530
+ ...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.`],
24046
24531
  `For completed work, finish by calling TaskOutput. Otherwise return only JSON matching these fields: ${Object.keys(SubagentResultSchema.shape).join(", ")}.${repair}`
24047
24532
  ].join("\n");
24048
24533
  for await (const event of child.loop.run({ prompt, signal: childSignal })) {
@@ -24478,7 +24963,7 @@ import { basename as basename9, join as join39, resolve as resolve33 } from "pat
24478
24963
  // src/d2c/workflow.ts
24479
24964
  import { createHash as createHash17 } from "crypto";
24480
24965
  import { join as join30 } from "path";
24481
- import { z as z37 } from "zod";
24966
+ import { z as z39 } from "zod";
24482
24967
 
24483
24968
  // src/d2c/workflow-shared.ts
24484
24969
  function reviewProgress(workflow) {
@@ -24501,91 +24986,91 @@ function buildD2cQualityRepairPrompt(task, issue) {
24501
24986
  }
24502
24987
 
24503
24988
  // src/d2c/workflow.ts
24504
- var ReviewSchema2 = z37.object({
24505
- fingerprint: z37.string().min(1).max(256),
24506
- pageId: z37.string().min(1).max(128),
24507
- reportId: z37.string().min(1).max(128),
24508
- signature: z37.string().regex(/^[a-f0-9]{64}$/),
24509
- label: z37.string().min(1).max(1e3),
24510
- decision: z37.enum(["pending", "accepted", "needs-fix"]),
24511
- instruction: z37.string().max(1e4).optional(),
24512
- moduleId: z37.string().min(1).max(128).optional(),
24513
- moduleSourceFiles: z37.array(z37.string().min(1).max(2048)).max(64).optional(),
24514
- updatedAt: z37.iso.datetime()
24989
+ var ReviewSchema2 = z39.object({
24990
+ fingerprint: z39.string().min(1).max(256),
24991
+ pageId: z39.string().min(1).max(128),
24992
+ reportId: z39.string().min(1).max(128),
24993
+ signature: z39.string().regex(/^[a-f0-9]{64}$/),
24994
+ label: z39.string().min(1).max(1e3),
24995
+ decision: z39.enum(["pending", "accepted", "needs-fix"]),
24996
+ instruction: z39.string().max(1e4).optional(),
24997
+ moduleId: z39.string().min(1).max(128).optional(),
24998
+ moduleSourceFiles: z39.array(z39.string().min(1).max(2048)).max(64).optional(),
24999
+ updatedAt: z39.iso.datetime()
24515
25000
  }).strict();
24516
- var InteractionRunSchema = z37.object({
24517
- schema: z37.literal(1),
24518
- runAt: z37.iso.datetime(),
24519
- baseUrl: z37.string().url().max(2048),
24520
- passed: z37.boolean(),
24521
- total: z37.number().int().nonnegative().max(5e4),
24522
- failures: z37.number().int().nonnegative().max(5e4),
24523
- apiRequestCount: z37.number().int().nonnegative().max(1e7),
24524
- scenarios: z37.array(z37.object({
24525
- id: z37.string().min(1).max(128),
24526
- pageUrl: z37.string().url().max(2048),
24527
- passed: z37.boolean(),
24528
- durationMs: z37.number().int().nonnegative().max(864e5),
24529
- apiRequestCount: z37.number().int().nonnegative().max(1e7),
24530
- failure: z37.string().max(2e4).optional(),
24531
- requests: z37.array(z37.object({ method: z37.string().min(1).max(16), path: z37.string().min(1).max(2048), status: z37.number().int().min(0).max(599) }).strict()).max(1e4).optional()
25001
+ var InteractionRunSchema = z39.object({
25002
+ schema: z39.literal(1),
25003
+ runAt: z39.iso.datetime(),
25004
+ baseUrl: z39.string().url().max(2048),
25005
+ passed: z39.boolean(),
25006
+ total: z39.number().int().nonnegative().max(5e4),
25007
+ failures: z39.number().int().nonnegative().max(5e4),
25008
+ apiRequestCount: z39.number().int().nonnegative().max(1e7),
25009
+ scenarios: z39.array(z39.object({
25010
+ id: z39.string().min(1).max(128),
25011
+ pageUrl: z39.string().url().max(2048),
25012
+ passed: z39.boolean(),
25013
+ durationMs: z39.number().int().nonnegative().max(864e5),
25014
+ apiRequestCount: z39.number().int().nonnegative().max(1e7),
25015
+ failure: z39.string().max(2e4).optional(),
25016
+ requests: z39.array(z39.object({ method: z39.string().min(1).max(16), path: z39.string().min(1).max(2048), status: z39.number().int().min(0).max(599) }).strict()).max(1e4).optional()
24532
25017
  }).strict()).max(5e4),
24533
- evidenceMode: z37.enum(["contract", "autonomous", "contract-fallback"]).optional()
25018
+ evidenceMode: z39.enum(["contract", "autonomous", "contract-fallback"]).optional()
24534
25019
  }).strict();
24535
- var QualityJudgmentSchema = z37.object({
24536
- schema: z37.literal(1),
24537
- runAt: z37.iso.datetime(),
24538
- model: z37.string().min(1).max(256),
24539
- visualScore: z37.number().min(0).max(100),
24540
- interactionScore: z37.number().min(0).max(100),
24541
- rawVisualScore: z37.number().min(0).max(100).optional(),
24542
- rawInteractionScore: z37.number().min(0).max(100).optional(),
24543
- staticVisualScore: z37.number().min(0).max(100),
24544
- deterministicInteractionPassed: z37.boolean(),
24545
- overallScore: z37.number().min(0).max(100),
24546
- threshold: z37.number().min(0).max(100),
24547
- verdict: z37.enum(["pass", "fail"]),
24548
- confidence: z37.enum(["high", "medium", "low"]),
24549
- summary: z37.string().min(1).max(4e3),
24550
- strengths: z37.array(z37.string().min(1).max(1e3)).max(20),
24551
- evidenceMode: z37.enum(["contract", "autonomous", "contract-fallback"]).optional(),
24552
- issues: z37.array(z37.object({
24553
- category: z37.enum(["visual", "interaction", "accessibility", "reliability"]),
24554
- severity: z37.enum(["minor", "major", "critical"]),
24555
- description: z37.string().min(1).max(2e3),
24556
- evidence: z37.string().min(1).max(2e3).optional(),
24557
- recommendation: z37.string().min(1).max(2e3),
24558
- selector: z37.string().min(1).max(2048).optional(),
24559
- scoreImpact: z37.number().min(0).max(30).optional(),
24560
- id: z37.string().regex(/^quality-[a-f0-9]{20}$/).optional(),
24561
- decision: z37.enum(["pending", "skipped", "fixing"]).optional(),
24562
- updatedAt: z37.iso.datetime().optional()
25020
+ var QualityJudgmentSchema = z39.object({
25021
+ schema: z39.literal(1),
25022
+ runAt: z39.iso.datetime(),
25023
+ model: z39.string().min(1).max(256),
25024
+ visualScore: z39.number().min(0).max(100),
25025
+ interactionScore: z39.number().min(0).max(100),
25026
+ rawVisualScore: z39.number().min(0).max(100).optional(),
25027
+ rawInteractionScore: z39.number().min(0).max(100).optional(),
25028
+ staticVisualScore: z39.number().min(0).max(100),
25029
+ deterministicInteractionPassed: z39.boolean(),
25030
+ overallScore: z39.number().min(0).max(100),
25031
+ threshold: z39.number().min(0).max(100),
25032
+ verdict: z39.enum(["pass", "fail"]),
25033
+ confidence: z39.enum(["high", "medium", "low"]),
25034
+ summary: z39.string().min(1).max(4e3),
25035
+ strengths: z39.array(z39.string().min(1).max(1e3)).max(20),
25036
+ evidenceMode: z39.enum(["contract", "autonomous", "contract-fallback"]).optional(),
25037
+ issues: z39.array(z39.object({
25038
+ category: z39.enum(["visual", "interaction", "accessibility", "reliability"]),
25039
+ severity: z39.enum(["minor", "major", "critical"]),
25040
+ description: z39.string().min(1).max(2e3),
25041
+ evidence: z39.string().min(1).max(2e3).optional(),
25042
+ recommendation: z39.string().min(1).max(2e3),
25043
+ selector: z39.string().min(1).max(2048).optional(),
25044
+ scoreImpact: z39.number().min(0).max(30).optional(),
25045
+ id: z39.string().regex(/^quality-[a-f0-9]{20}$/).optional(),
25046
+ decision: z39.enum(["pending", "skipped", "fixing"]).optional(),
25047
+ updatedAt: z39.iso.datetime().optional()
24563
25048
  }).strict()).max(100)
24564
25049
  }).strict();
24565
- var WorkflowSchema = z37.object({
24566
- schema: z37.literal(1),
24567
- task: z37.string().regex(D2C_TASK_PATTERN),
24568
- revision: z37.number().int().nonnegative(),
24569
- stage: z37.enum(["visual-review", "api-mapping", "integrating", "interaction-review", "quality-judge", "completed"]),
24570
- framework: z37.enum(["vue", "react"]),
24571
- activeReportId: z37.string().min(1).max(128),
24572
- activeBatchId: z37.string().min(1).max(128).optional(),
24573
- implementationDir: z37.string().min(1).max(32768),
24574
- reviews: z37.array(ReviewSchema2).max(1e4),
24575
- openapi: z37.object({
24576
- sourceName: z37.string().min(1).max(255),
24577
- importedAt: z37.iso.datetime(),
24578
- hash: z37.string().regex(/^[a-f0-9]{64}$/),
24579
- version: z37.string().min(1).max(32),
24580
- title: z37.string().min(1).max(500),
24581
- baseUrl: z37.string().max(4096).optional()
25050
+ var WorkflowSchema = z39.object({
25051
+ schema: z39.literal(1),
25052
+ task: z39.string().regex(D2C_TASK_PATTERN),
25053
+ revision: z39.number().int().nonnegative(),
25054
+ stage: z39.enum(["visual-review", "api-mapping", "integrating", "interaction-review", "quality-judge", "completed"]),
25055
+ framework: z39.enum(["vue", "react"]),
25056
+ activeReportId: z39.string().min(1).max(128),
25057
+ activeBatchId: z39.string().min(1).max(128).optional(),
25058
+ implementationDir: z39.string().min(1).max(32768),
25059
+ reviews: z39.array(ReviewSchema2).max(1e4),
25060
+ openapi: z39.object({
25061
+ sourceName: z39.string().min(1).max(255),
25062
+ importedAt: z39.iso.datetime(),
25063
+ hash: z39.string().regex(/^[a-f0-9]{64}$/),
25064
+ version: z39.string().min(1).max(32),
25065
+ title: z39.string().min(1).max(500),
25066
+ baseUrl: z39.string().max(4096).optional()
24582
25067
  }).strict().optional(),
24583
- mappings: z37.array(z37.unknown()).max(5e3).optional(),
24584
- integrationFiles: z37.array(z37.string().min(1).max(2048)).max(1e3).optional(),
24585
- interaction: z37.object({ manualDecision: z37.enum(["pending", "accepted"]), automated: InteractionRunSchema.optional(), updatedAt: z37.iso.datetime() }).strict().optional(),
25068
+ mappings: z39.array(z39.unknown()).max(5e3).optional(),
25069
+ integrationFiles: z39.array(z39.string().min(1).max(2048)).max(1e3).optional(),
25070
+ interaction: z39.object({ manualDecision: z39.enum(["pending", "accepted"]), automated: InteractionRunSchema.optional(), updatedAt: z39.iso.datetime() }).strict().optional(),
24586
25071
  quality: QualityJudgmentSchema.optional(),
24587
- qualityWaivers: z37.array(z37.object({ issueId: z37.string().regex(/^quality-[a-f0-9]{20}$/), updatedAt: z37.iso.datetime() }).strict()).max(100).optional(),
24588
- updatedAt: z37.iso.datetime()
25072
+ qualityWaivers: z39.array(z39.object({ issueId: z39.string().regex(/^quality-[a-f0-9]{20}$/), updatedAt: z39.iso.datetime() }).strict()).max(100).optional(),
25073
+ updatedAt: z39.iso.datetime()
24589
25074
  }).strict();
24590
25075
  function pageId(report) {
24591
25076
  return report.page?.id ?? "index";
@@ -25002,16 +25487,16 @@ function confirmApiMapping(mapping, operationKey, operations) {
25002
25487
  // src/d2c/modules.ts
25003
25488
  import { readFile as readFile26 } from "fs/promises";
25004
25489
  import { isAbsolute as isAbsolute15, join as join31, normalize as normalize2, posix, sep as sep23, win32 as win322 } from "path";
25005
- import { z as z38 } from "zod";
25006
- var ModuleSchema = z38.object({
25007
- id: z38.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
25008
- label: z38.string().trim().min(1).max(200),
25009
- sourceFiles: z38.array(z38.string().trim().min(1).max(2048)).min(1).max(64),
25010
- keywords: z38.array(z38.string().trim().min(1).max(100)).max(100).optional(),
25011
- dataNeeds: z38.array(z38.string().trim().min(1).max(100)).max(100).optional(),
25012
- actions: z38.array(z38.string().trim().min(1).max(100)).max(100).optional()
25490
+ import { z as z40 } from "zod";
25491
+ var ModuleSchema = z40.object({
25492
+ id: z40.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
25493
+ label: z40.string().trim().min(1).max(200),
25494
+ sourceFiles: z40.array(z40.string().trim().min(1).max(2048)).min(1).max(64),
25495
+ keywords: z40.array(z40.string().trim().min(1).max(100)).max(100).optional(),
25496
+ dataNeeds: z40.array(z40.string().trim().min(1).max(100)).max(100).optional(),
25497
+ actions: z40.array(z40.string().trim().min(1).max(100)).max(100).optional()
25013
25498
  }).strict();
25014
- var ManifestSchema3 = z38.object({ schema: z38.literal(1), modules: z38.array(ModuleSchema).min(1).max(500) }).passthrough();
25499
+ var ManifestSchema3 = z40.object({ schema: z40.literal(1), modules: z40.array(ModuleSchema).min(1).max(500) }).passthrough();
25015
25500
  function safeSource(path) {
25016
25501
  if (isAbsolute15(path) || posix.isAbsolute(path) || win322.isAbsolute(path) || path.includes("\0")) return false;
25017
25502
  const value = normalize2(path);
@@ -25453,7 +25938,7 @@ ${managed.output().slice(-4e3)}`);
25453
25938
  import { createHash as createHash19, randomUUID as randomUUID23 } from "crypto";
25454
25939
  import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile30, rename as rename13, stat as stat12, unlink as unlink6, writeFile as writeFile15 } from "fs/promises";
25455
25940
  import { join as join34 } from "path";
25456
- import { z as z39 } from "zod";
25941
+ import { z as z41 } from "zod";
25457
25942
 
25458
25943
  // src/e2e/prd-governance.ts
25459
25944
  import { createHash as createHash18, randomUUID as randomUUID22 } from "crypto";
@@ -25572,38 +26057,38 @@ var RelativePrototypePath = "product/prototype/index.html";
25572
26057
  var RelativeInteractionPath = "product/prototype/interaction-manifest.json";
25573
26058
  var defaultPlanFileOperations = { rename: rename13, copyFile: copyFile2, unlink: unlink6 };
25574
26059
  var planWriteQueues = /* @__PURE__ */ new Map();
25575
- var PlanSchema2 = z39.object({
25576
- schema: z39.literal(1),
25577
- task: z39.string().regex(D2C_TASK_PATTERN),
25578
- revision: z39.number().int().nonnegative(),
25579
- phase: z39.enum(["prd-generating", "prd-review", "design-generating", "design-review", "ready-for-d2c"]),
25580
- framework: z39.enum(["vue", "react"]),
25581
- technology: z39.object({
25582
- frontend: z39.string().trim().min(1).max(64),
25583
- backend: z39.string().trim().min(1).max(64),
25584
- frontendSource: z39.enum(["default", "requirement"]),
25585
- backendSource: z39.enum(["default", "requirement"])
26060
+ var PlanSchema2 = z41.object({
26061
+ schema: z41.literal(1),
26062
+ task: z41.string().regex(D2C_TASK_PATTERN),
26063
+ revision: z41.number().int().nonnegative(),
26064
+ phase: z41.enum(["prd-generating", "prd-review", "design-generating", "design-review", "ready-for-d2c"]),
26065
+ framework: z41.enum(["vue", "react"]),
26066
+ technology: z41.object({
26067
+ frontend: z41.string().trim().min(1).max(64),
26068
+ backend: z41.string().trim().min(1).max(64),
26069
+ frontendSource: z41.enum(["default", "requirement"]),
26070
+ backendSource: z41.enum(["default", "requirement"])
25586
26071
  }).strict().optional(),
25587
- requirement: z39.string().trim().min(2).max(5e4),
25588
- prd: z39.object({ path: z39.literal(RelativePrdPath), updatedAt: z39.iso.datetime(), contentHash: z39.string().regex(/^[a-f0-9]{64}$/).optional() }).strict().optional(),
25589
- approvedPrd: z39.object({
25590
- hash: z39.string().regex(/^[a-f0-9]{64}$/),
25591
- approvedAt: z39.iso.datetime(),
25592
- criteria: z39.array(z39.object({ id: z39.string().regex(/^AC-\d{3,}$/), text: z39.string().trim().min(1).max(4e3) }).strict()).min(1).max(1e4)
26072
+ requirement: z41.string().trim().min(2).max(5e4),
26073
+ prd: z41.object({ path: z41.literal(RelativePrdPath), updatedAt: z41.iso.datetime(), contentHash: z41.string().regex(/^[a-f0-9]{64}$/).optional() }).strict().optional(),
26074
+ approvedPrd: z41.object({
26075
+ hash: z41.string().regex(/^[a-f0-9]{64}$/),
26076
+ approvedAt: z41.iso.datetime(),
26077
+ criteria: z41.array(z41.object({ id: z41.string().regex(/^AC-\d{3,}$/), text: z41.string().trim().min(1).max(4e3) }).strict()).min(1).max(1e4)
25593
26078
  }).strict().optional(),
25594
- prototype: z39.object({
25595
- entryHtml: z39.literal(RelativePrototypePath),
25596
- interactionManifest: z39.literal(RelativeInteractionPath),
25597
- updatedAt: z39.iso.datetime(),
25598
- contentHash: z39.string().regex(/^[a-f0-9]{64}$/).optional()
26079
+ prototype: z41.object({
26080
+ entryHtml: z41.literal(RelativePrototypePath),
26081
+ interactionManifest: z41.literal(RelativeInteractionPath),
26082
+ updatedAt: z41.iso.datetime(),
26083
+ contentHash: z41.string().regex(/^[a-f0-9]{64}$/).optional()
25599
26084
  }).strict().optional(),
25600
- feedback: z39.object({
25601
- stage: z39.enum(["prd", "design"]),
25602
- message: z39.string().trim().min(1).max(1e4),
25603
- updatedAt: z39.iso.datetime()
26085
+ feedback: z41.object({
26086
+ stage: z41.enum(["prd", "design"]),
26087
+ message: z41.string().trim().min(1).max(1e4),
26088
+ updatedAt: z41.iso.datetime()
25604
26089
  }).strict().optional(),
25605
- createdAt: z39.iso.datetime(),
25606
- updatedAt: z39.iso.datetime()
26090
+ createdAt: z41.iso.datetime(),
26091
+ updatedAt: z41.iso.datetime()
25607
26092
  }).strict();
25608
26093
  function d2cProductDirectory(workspace, task) {
25609
26094
  if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
@@ -25764,10 +26249,10 @@ async function writeD2cProductPlan(workspace, plan, operations = defaultPlanFile
25764
26249
  return parsed;
25765
26250
  }
25766
26251
  async function createD2cProductPlan(workspace, input, now = () => /* @__PURE__ */ new Date()) {
25767
- const parsed = z39.object({
25768
- task: z39.string().regex(D2C_TASK_PATTERN),
25769
- framework: z39.enum(["vue", "react"]),
25770
- requirement: z39.string().trim().min(2).max(5e4)
26252
+ const parsed = z41.object({
26253
+ task: z41.string().regex(D2C_TASK_PATTERN),
26254
+ framework: z41.enum(["vue", "react"]),
26255
+ requirement: z41.string().trim().min(2).max(5e4)
25771
26256
  }).strict().parse(input);
25772
26257
  const timestamp2 = now().toISOString();
25773
26258
  if (await readD2cProductPlan(workspace, parsed.task) !== void 0) {
@@ -25999,31 +26484,31 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
25999
26484
  // src/e2e/delivery-run.ts
26000
26485
  import { createHash as createHash21 } from "crypto";
26001
26486
  import { join as join35 } from "path";
26002
- import { z as z40 } from "zod";
26487
+ import { z as z42 } from "zod";
26003
26488
  var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
26004
- var ArtifactSchema = z40.object({
26005
- path: z40.string().min(1).max(32768),
26006
- hash: z40.string().regex(/^[a-f0-9]{64}$/),
26007
- bytes: z40.number().int().nonnegative(),
26008
- createdAt: z40.iso.datetime()
26489
+ var ArtifactSchema = z42.object({
26490
+ path: z42.string().min(1).max(32768),
26491
+ hash: z42.string().regex(/^[a-f0-9]{64}$/),
26492
+ bytes: z42.number().int().nonnegative(),
26493
+ createdAt: z42.iso.datetime()
26009
26494
  }).strict();
26010
- var NodeSchema = z40.object({
26011
- id: z40.enum(DELIVERY_NODE_IDS),
26012
- status: z40.enum(["pending", "running", "waiting-approval", "succeeded", "failed", "stale"]),
26013
- attempt: z40.number().int().nonnegative(),
26014
- inputs: z40.array(ArtifactSchema).max(1e4),
26015
- outputs: z40.array(ArtifactSchema).max(1e4),
26016
- startedAt: z40.iso.datetime().optional(),
26017
- completedAt: z40.iso.datetime().optional(),
26018
- error: z40.string().max(2e4).optional()
26495
+ var NodeSchema = z42.object({
26496
+ id: z42.enum(DELIVERY_NODE_IDS),
26497
+ status: z42.enum(["pending", "running", "waiting-approval", "succeeded", "failed", "stale"]),
26498
+ attempt: z42.number().int().nonnegative(),
26499
+ inputs: z42.array(ArtifactSchema).max(1e4),
26500
+ outputs: z42.array(ArtifactSchema).max(1e4),
26501
+ startedAt: z42.iso.datetime().optional(),
26502
+ completedAt: z42.iso.datetime().optional(),
26503
+ error: z42.string().max(2e4).optional()
26019
26504
  }).strict();
26020
- var DeliveryRunSchema = z40.object({
26021
- schema: z40.literal(1),
26022
- task: z40.string().regex(D2C_TASK_PATTERN),
26023
- revision: z40.number().int().nonnegative(),
26024
- nodes: z40.record(z40.enum(DELIVERY_NODE_IDS), NodeSchema),
26025
- createdAt: z40.iso.datetime(),
26026
- updatedAt: z40.iso.datetime()
26505
+ var DeliveryRunSchema = z42.object({
26506
+ schema: z42.literal(1),
26507
+ task: z42.string().regex(D2C_TASK_PATTERN),
26508
+ revision: z42.number().int().nonnegative(),
26509
+ nodes: z42.record(z42.enum(DELIVERY_NODE_IDS), NodeSchema),
26510
+ createdAt: z42.iso.datetime(),
26511
+ updatedAt: z42.iso.datetime()
26027
26512
  }).strict();
26028
26513
  var dependencies = {
26029
26514
  requirement: [],
@@ -27862,7 +28347,69 @@ function desktopWindowChrome(platform = process.platform) {
27862
28347
  };
27863
28348
  }
27864
28349
 
28350
+ // src/utils/crash-guard.ts
28351
+ import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
28352
+ import { join as join40 } from "path";
28353
+ var installed = false;
28354
+ function installCrashGuard(options = {}) {
28355
+ if (installed) return;
28356
+ installed = true;
28357
+ const crashLogDir = () => {
28358
+ try {
28359
+ return join40((options.workspace ?? process.cwd)(), ".flavor");
28360
+ } catch {
28361
+ return process.cwd();
28362
+ }
28363
+ };
28364
+ const writeCrashLog = (kind, detail) => {
28365
+ try {
28366
+ const dir = crashLogDir();
28367
+ mkdirSync2(dir, { recursive: true });
28368
+ const path = join40(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
28369
+ writeFileSync(path, [
28370
+ `kind: ${kind}`,
28371
+ `version: ${process.version}`,
28372
+ `platform: ${process.platform}`,
28373
+ `time: ${(/* @__PURE__ */ new Date()).toISOString()}`,
28374
+ "",
28375
+ detail
28376
+ ].join("\n"), { encoding: "utf8", mode: 384 });
28377
+ return path;
28378
+ } catch {
28379
+ return void 0;
28380
+ }
28381
+ };
28382
+ const restoreTerminal = () => {
28383
+ try {
28384
+ if (process.stdout.isTTY === true) process.stdout.write("\x1B[?25h\x1B[?1049l");
28385
+ } catch {
28386
+ }
28387
+ };
28388
+ const describe = (reason) => {
28389
+ if (reason instanceof Error) return `${reason.name}: ${reason.message}
28390
+ ${reason.stack ?? ""}`;
28391
+ return String(reason);
28392
+ };
28393
+ const crash = (kind, reason) => {
28394
+ const detail = describe(reason);
28395
+ const logPath = writeCrashLog(kind, detail);
28396
+ restoreTerminal();
28397
+ try {
28398
+ const safe = redactErrorText(detail.split("\n").slice(0, 3).join("\n"));
28399
+ process.stderr.write(`flavor crashed (${kind}): ${safe}
28400
+ `);
28401
+ if (logPath !== void 0) process.stderr.write(`crash log: ${logPath}
28402
+ `);
28403
+ } catch {
28404
+ }
28405
+ process.exit(1);
28406
+ };
28407
+ process.on("unhandledRejection", (reason) => crash("unhandledRejection", reason));
28408
+ process.on("uncaughtException", (error) => crash("uncaughtException", error));
28409
+ }
28410
+
27865
28411
  // src/desktop/main.ts
28412
+ installCrashGuard();
27866
28413
  var moduleDirectory = dirname23(fileURLToPath4(import.meta.url));
27867
28414
  var developmentUrl = process.env.FLAVOR_DESKTOP_DEV_URL;
27868
28415
  var mainWindow;
@@ -27870,7 +28417,7 @@ var appMenu;
27870
28417
  var quitting = false;
27871
28418
  function logStartup(step, detail) {
27872
28419
  try {
27873
- const logPath = join40(dirname23(process.execPath), "flavor-code-startup.log");
28420
+ const logPath = join41(dirname23(process.execPath), "flavor-code-startup.log");
27874
28421
  const ts = (/* @__PURE__ */ new Date()).toISOString();
27875
28422
  appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
27876
28423
  `);
@@ -27895,7 +28442,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
27895
28442
  };
27896
28443
  });
27897
28444
  var d2cJudgeClient = createD2cJudgeClient();
27898
- var judgeStore = () => createD2cJudgeConfigStore(join40(app.getPath("userData"), "d2c-judge.json"));
28445
+ var judgeStore = () => createD2cJudgeConfigStore(join41(app.getPath("userData"), "d2c-judge.json"));
27899
28446
  function judgeImage(png) {
27900
28447
  const image = nativeImage.createFromBuffer(png);
27901
28448
  const size = image.getSize();
@@ -27945,7 +28492,7 @@ var controller = new DesktopRuntimeController({
27945
28492
  })
27946
28493
  });
27947
28494
  function statePath() {
27948
- return join40(app.getPath("userData"), "desktop-state.json");
28495
+ return join41(app.getPath("userData"), "desktop-state.json");
27949
28496
  }
27950
28497
  async function loadPersistedWorkspace() {
27951
28498
  try {
@@ -27980,7 +28527,7 @@ async function chooseAndOpenWorkspace() {
27980
28527
  var cachedAppIconDataUrl;
27981
28528
  function appIconDataUrl() {
27982
28529
  if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
27983
- const icon = nativeImage.createFromPath(join40(app.getAppPath(), "assets", "icon.png"));
28530
+ const icon = nativeImage.createFromPath(join41(app.getAppPath(), "assets", "icon.png"));
27984
28531
  if (icon.isEmpty()) return void 0;
27985
28532
  cachedAppIconDataUrl = icon.toDataURL();
27986
28533
  return cachedAppIconDataUrl;
@@ -28225,7 +28772,7 @@ function applicationMenu() {
28225
28772
  ]);
28226
28773
  }
28227
28774
  async function createWindow() {
28228
- const rendererPath = join40(app.getAppPath(), "dist", "desktop-renderer", "index.html");
28775
+ const rendererPath = join41(app.getAppPath(), "dist", "desktop-renderer", "index.html");
28229
28776
  mainWindow = new BrowserWindow2({
28230
28777
  title: "Flavor Code",
28231
28778
  width: 1280,
@@ -28236,14 +28783,14 @@ async function createWindow() {
28236
28783
  show: false,
28237
28784
  ...desktopWindowChrome(),
28238
28785
  webPreferences: {
28239
- preload: join40(moduleDirectory, "preload.cjs"),
28786
+ preload: join41(moduleDirectory, "preload.cjs"),
28240
28787
  contextIsolation: true,
28241
28788
  nodeIntegration: false,
28242
28789
  sandbox: false,
28243
28790
  webSecurity: true
28244
28791
  }
28245
28792
  });
28246
- const iconPath = join40(app.getAppPath(), "assets", "icon.png");
28793
+ const iconPath = join41(app.getAppPath(), "assets", "icon.png");
28247
28794
  const icon = nativeImage.createFromPath(iconPath);
28248
28795
  if (!icon.isEmpty()) {
28249
28796
  mainWindow.setIcon(icon);