@voybio/ace-swarm 2.4.3 → 2.4.6

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.
Files changed (144) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +72 -532
  3. package/assets/agent-state/INTERFACE_REGISTRY.md +2 -2
  4. package/assets/agent-state/MODULES/gates/gate-autonomy.json +4 -2
  5. package/assets/agent-state/MODULES/gates/gate-completeness.json +4 -2
  6. package/assets/agent-state/MODULES/gates/gate-operability.json +4 -2
  7. package/assets/agent-state/MODULES/gates/gate-security.json +3 -1
  8. package/assets/agent-state/MODULES/registry.json +1 -4
  9. package/assets/agent-state/MODULES/roles/capability-build.json +1 -2
  10. package/assets/agent-state/MODULES/roles/capability-eval.json +1 -1
  11. package/assets/agent-state/MODULES/roles/capability-qa.json +1 -2
  12. package/assets/agent-state/QUALITY_GATES.md +25 -9
  13. package/assets/agent-state/TEAL_CONFIG.md +2 -11
  14. package/assets/eval-battery/battery.json +85 -0
  15. package/assets/eval-battery/fixtures/edit-target.js +3 -0
  16. package/assets/scripts/render-mcp-configs.sh +12 -0
  17. package/dist/ace-context.d.ts +18 -0
  18. package/dist/ace-context.js +161 -16
  19. package/dist/ace-server-instructions.js +36 -9
  20. package/dist/astgrep-index.d.ts +7 -1
  21. package/dist/astgrep-index.js +66 -39
  22. package/dist/bridge-trace.d.ts +17 -0
  23. package/dist/bridge-trace.js +23 -0
  24. package/dist/cli.d.ts +9 -1
  25. package/dist/cli.js +619 -10
  26. package/dist/discovery-runtime-wrappers.d.ts +16 -1
  27. package/dist/discovery-runtime-wrappers.js +65 -9
  28. package/dist/eval-harness.d.ts +110 -0
  29. package/dist/eval-harness.js +254 -0
  30. package/dist/experience-reflector.d.ts +13 -0
  31. package/dist/experience-reflector.js +62 -0
  32. package/dist/gate-contract.d.ts +63 -0
  33. package/dist/gate-contract.js +178 -0
  34. package/dist/goal-engine.d.ts +155 -0
  35. package/dist/goal-engine.js +795 -0
  36. package/dist/helpers/store-resolution.js +40 -39
  37. package/dist/hermes/session-manager.d.ts +10 -0
  38. package/dist/hermes/session-manager.js +130 -27
  39. package/dist/job-scheduler.d.ts +58 -0
  40. package/dist/job-scheduler.js +202 -2
  41. package/dist/job-worker.d.ts +87 -0
  42. package/dist/job-worker.js +182 -0
  43. package/dist/local-model-policy.d.ts +8 -2
  44. package/dist/local-model-policy.js +31 -17
  45. package/dist/local-model-runtime.d.ts +6 -0
  46. package/dist/local-model-runtime.js +130 -2
  47. package/dist/measured-classing.d.ts +66 -0
  48. package/dist/measured-classing.js +165 -0
  49. package/dist/model-bridge.d.ts +45 -0
  50. package/dist/model-bridge.js +502 -44
  51. package/dist/orchestrator-supervisor.js +1 -1
  52. package/dist/plan-proposal.d.ts +7 -0
  53. package/dist/plan-proposal.js +74 -4
  54. package/dist/playbook-adaptation.d.ts +37 -0
  55. package/dist/playbook-adaptation.js +159 -0
  56. package/dist/playbook-curator.d.ts +16 -0
  57. package/dist/playbook-curator.js +106 -0
  58. package/dist/playbook-llm-reflector.d.ts +26 -0
  59. package/dist/playbook-llm-reflector.js +157 -0
  60. package/dist/public-surface.d.ts +2 -0
  61. package/dist/public-surface.js +26 -14
  62. package/dist/runtime-executor.js +45 -2
  63. package/dist/runtime-tool-specs.d.ts +8 -1
  64. package/dist/runtime-tool-specs.js +19 -1
  65. package/dist/safe-edit.d.ts +20 -0
  66. package/dist/safe-edit.js +355 -78
  67. package/dist/schemas.js +16 -15
  68. package/dist/server.d.ts +11 -2
  69. package/dist/server.js +55 -5
  70. package/dist/shared.d.ts +12 -0
  71. package/dist/shared.js +13 -1
  72. package/dist/source-mutation-coordinator.d.ts +54 -0
  73. package/dist/source-mutation-coordinator.js +259 -0
  74. package/dist/status-events.d.ts +4 -1
  75. package/dist/status-events.js +41 -6
  76. package/dist/store/ace-packed-store.d.ts +28 -9
  77. package/dist/store/ace-packed-store.js +324 -57
  78. package/dist/store/bgzf.d.ts +4 -0
  79. package/dist/store/bgzf.js +94 -0
  80. package/dist/store/bootstrap-store.d.ts +4 -1
  81. package/dist/store/bootstrap-store.js +87 -9
  82. package/dist/store/cache-workspace.d.ts +8 -0
  83. package/dist/store/cache-workspace.js +67 -1
  84. package/dist/store/crc32.d.ts +2 -0
  85. package/dist/store/crc32.js +16 -0
  86. package/dist/store/fsck.d.ts +16 -0
  87. package/dist/store/fsck.js +240 -0
  88. package/dist/store/gate-contract-migration.d.ts +10 -0
  89. package/dist/store/gate-contract-migration.js +413 -0
  90. package/dist/store/materializers/context-snapshot-materializer.d.ts +1 -0
  91. package/dist/store/materializers/context-snapshot-materializer.js +4 -1
  92. package/dist/store/materializers/projection-manager.d.ts +7 -0
  93. package/dist/store/materializers/projection-manager.js +37 -0
  94. package/dist/store/materializers/scheduler-projection-materializer.js +10 -3
  95. package/dist/store/repositories/goal-repository.d.ts +112 -0
  96. package/dist/store/repositories/goal-repository.js +157 -0
  97. package/dist/store/repositories/playbook-repository.d.ts +107 -0
  98. package/dist/store/repositories/playbook-repository.js +314 -0
  99. package/dist/store/store-artifacts.js +44 -2
  100. package/dist/store/store-authority-audit.js +1 -0
  101. package/dist/store/store-cli.d.ts +30 -0
  102. package/dist/store/store-cli.js +242 -0
  103. package/dist/store/store-snapshot.d.ts +12 -0
  104. package/dist/store/store-snapshot.js +74 -11
  105. package/dist/store/types.d.ts +1 -0
  106. package/dist/store/write-coordinator.d.ts +7 -0
  107. package/dist/store/write-coordinator.js +11 -0
  108. package/dist/todo-state.d.ts +21 -0
  109. package/dist/todo-state.js +150 -53
  110. package/dist/tools-agent.js +86 -22
  111. package/dist/tools-discovery.js +5 -3
  112. package/dist/tools-files.js +107 -23
  113. package/dist/tools-framework.js +146 -40
  114. package/dist/tools-goal.d.ts +11 -0
  115. package/dist/tools-goal.js +108 -0
  116. package/dist/tools-handoff.js +7 -11
  117. package/dist/tools-lifecycle.js +2 -0
  118. package/dist/tools-scheduler.js +44 -1
  119. package/dist/tools-skills.js +3 -2
  120. package/dist/tools-todo.js +64 -3
  121. package/dist/tools.js +2 -0
  122. package/dist/tui/chat.js +4 -0
  123. package/dist/tui/index.js +4 -1
  124. package/dist/tui/ollama.d.ts +2 -0
  125. package/dist/tui/openai-compatible.d.ts +14 -0
  126. package/dist/tui/openai-compatible.js +15 -2
  127. package/dist/tui/provider-discovery.d.ts +1 -0
  128. package/dist/tui/provider-discovery.js +2 -2
  129. package/docs/cli-reference.md +125 -0
  130. package/docs/development.md +46 -0
  131. package/docs/evaluation-safety.md +27 -0
  132. package/docs/experience-playbook.md +35 -0
  133. package/docs/hermes.md +129 -0
  134. package/docs/library-api.md +31 -0
  135. package/docs/mcp-tool-reference.md +290 -0
  136. package/docs/provider-security.md +85 -0
  137. package/docs/state-model.md +113 -0
  138. package/docs/troubleshooting.md +95 -0
  139. package/docs/tui-operator-guide.md +75 -0
  140. package/package.json +5 -1
  141. package/scripts/dev/run_hermes_crispr_live.mjs +212 -0
  142. package/assets/agent-state/MODULES/gates/gate-correctness.json +0 -7
  143. package/assets/agent-state/MODULES/gates/gate-evaluation.json +0 -7
  144. package/assets/agent-state/MODULES/gates/gate-typescript-public-surface.json +0 -7
@@ -1,7 +1,7 @@
1
- import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
2
  import { dirname, extname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { PACKAGE_ROOT, resolveWorkspaceRoot } from "./helpers.js";
4
+ import { PACKAGE_ROOT, resolveWorkspaceRoot, safeWrite } from "./helpers.js";
5
5
  import { EVENT_TYPE } from "./shared.js";
6
6
  export const PUBLIC_SURFACE_REPORT_REL_PATH = "agent-state/PUBLIC_SURFACE_REPORT.md";
7
7
  const SCHEMA_REQUIREMENTS = {
@@ -116,13 +116,6 @@ function workspaceRoot() {
116
116
  function reportPath(relPath = PUBLIC_SURFACE_REPORT_REL_PATH) {
117
117
  return resolve(workspaceRoot(), relPath);
118
118
  }
119
- function safeWriteReport(filePath, content) {
120
- mkdirSync(dirname(filePath), { recursive: true });
121
- const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
122
- writeFileSync(tmpPath, content, "utf-8");
123
- renameSync(tmpPath, filePath);
124
- return filePath;
125
- }
126
119
  async function collectSurfaceEntries() {
127
120
  const entries = [];
128
121
  const [{ registerTools }, { registerResources }, { registerPrompts }] = await Promise.all([
@@ -162,7 +155,7 @@ async function collectSurfaceEntries() {
162
155
  registerPrompts(stub);
163
156
  return entries.sort((left, right) => left.id.localeCompare(right.id));
164
157
  }
165
- function walkTypescriptFiles(root) {
158
+ function walkRuntimeSourceFiles(root) {
166
159
  const files = [];
167
160
  const visit = (dirPath) => {
168
161
  const entries = readdirSync(dirPath, { withFileTypes: true });
@@ -176,8 +169,9 @@ function walkTypescriptFiles(root) {
176
169
  visit(absolutePath);
177
170
  continue;
178
171
  }
179
- if (extname(entry.name) === ".ts")
172
+ if ([".ts", ".js"].includes(extname(entry.name)) && !entry.name.endsWith(".d.ts")) {
180
173
  files.push(absolutePath);
174
+ }
181
175
  }
182
176
  };
183
177
  visit(root);
@@ -185,7 +179,7 @@ function walkTypescriptFiles(root) {
185
179
  }
186
180
  function collectEmittedEventTypes() {
187
181
  const srcRoot = dirname(fileURLToPath(import.meta.url));
188
- const files = walkTypescriptFiles(srcRoot);
182
+ const files = walkRuntimeSourceFiles(srcRoot);
189
183
  const discovered = new Set();
190
184
  const patterns = [
191
185
  /event_type:\s*"([A-Z][A-Z0-9_]+)"/g,
@@ -272,10 +266,24 @@ export function auditPublicSurface(options) {
272
266
  const packageAbs = resolve(PACKAGE_ROOT, "assets", relPath);
273
267
  if (!existsSync(abs) && !existsSync(packageAbs)) {
274
268
  failures.push(`schema file missing for ${surfaceId}: ${relPath}`);
269
+ continue;
270
+ }
271
+ const schemaPath = existsSync(abs) ? abs : packageAbs;
272
+ try {
273
+ const schema = JSON.parse(readFileSync(schemaPath, "utf-8"));
274
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
275
+ failures.push(`schema file is not a JSON object for ${surfaceId}: ${relPath}`);
276
+ }
277
+ }
278
+ catch (error) {
279
+ failures.push(`schema file is invalid JSON for ${surfaceId}: ${relPath} (${error instanceof Error ? error.message : String(error)})`);
275
280
  }
276
281
  }
277
282
  }
278
283
  const eventRegistry = new Set(Object.values(EVENT_TYPE));
284
+ if (emittedEvents.length === 0) {
285
+ failures.push("no emitted event types discovered in the production runtime sources");
286
+ }
279
287
  for (const eventType of emittedEvents) {
280
288
  if (!eventRegistry.has(eventType)) {
281
289
  failures.push(`event type emitted without shared registry entry: ${eventType}`);
@@ -292,7 +300,8 @@ export function auditPublicSurface(options) {
292
300
  emitted_events: emittedEvents.length,
293
301
  schema_bound_surfaces: Object.keys(SCHEMA_REQUIREMENTS).length,
294
302
  };
295
- const report_path = reportPath(options?.artifact_path || PUBLIC_SURFACE_REPORT_REL_PATH);
303
+ const artifactRelPath = options?.artifact_path || PUBLIC_SURFACE_REPORT_REL_PATH;
304
+ const report_path = reportPath(artifactRelPath);
296
305
  const result = {
297
306
  ok: failures.length === 0,
298
307
  summary,
@@ -301,8 +310,9 @@ export function auditPublicSurface(options) {
301
310
  surfaces,
302
311
  emitted_events: emittedEvents,
303
312
  };
313
+ let written_path;
304
314
  if (options?.write_artifact !== false) {
305
- safeWriteReport(report_path, renderReport(result));
315
+ written_path = safeWrite(artifactRelPath, renderReport(result));
306
316
  }
307
317
  else if (!existsSync(report_path)) {
308
318
  warnings.push(`public surface report missing: ${report_path}`);
@@ -310,6 +320,8 @@ export function auditPublicSurface(options) {
310
320
  return {
311
321
  ...result,
312
322
  report_path,
323
+ artifact_path: artifactRelPath,
324
+ written_path,
313
325
  };
314
326
  });
315
327
  }
@@ -20,7 +20,7 @@ import { getWorkspaceStorePath, storeExistsSync, } from "./store/store-snapshot.
20
20
  import { operationalArtifactVirtualPath } from "./store/store-artifacts.js";
21
21
  import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
22
22
  import { LocalModelRuntimeRepository, withLocalModelRuntimeRepository, } from "./store/repositories/local-model-runtime-repository.js";
23
- import { proposePlanDeterministic, validatePlan } from "./plan-proposal.js";
23
+ import { loadAcceptanceTraceContract, proposePlanDeterministic, validatePlan } from "./plan-proposal.js";
24
24
  export const RUNTIME_EXECUTOR_SESSION_REGISTRY_REL_PATH = "agent-state/runtime-executor-sessions.json";
25
25
  export const RUNTIME_EXECUTOR_SESSION_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json";
26
26
  export const RUNTIME_EXECUTOR_SESSION_SCHEMA_NAME = "runtime-executor-session-registry@1.0.0";
@@ -583,6 +583,7 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
583
583
  }
584
584
  return;
585
585
  }
586
+ let mutationLanePolicy = { mutation_lane: "raw_write_allowed" };
586
587
  // ── Plan validation preflight (Task 6) ───────────────────────────────
587
588
  {
588
589
  const currentValidatedPlanId = registryStart.validated_plan_id;
@@ -592,6 +593,13 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
592
593
  const proposal = proposePlanDeterministic(task);
593
594
  const verdict = await validatePlan({ proposal, sessionId });
594
595
  if (verdict.ok) {
596
+ const structuralStep = proposal.steps.find((step) => step.structural_edit_plan_required === true);
597
+ if (structuralStep) {
598
+ mutationLanePolicy = {
599
+ mutation_lane: "structural_edit",
600
+ waiver: structuralStep.structural_edit_waiver ?? null,
601
+ };
602
+ }
595
603
  // Persist validated_plan_id opportunistically. This is a readiness hint,
596
604
  // not a hard gate on unattended execution.
597
605
  await mutateRegistry((registry) => {
@@ -606,6 +614,16 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
606
614
  // even when the planner/model path or trace lookup is unavailable.
607
615
  }
608
616
  }
617
+ else {
618
+ const contract = loadAcceptanceTraceContract(currentValidatedPlanId);
619
+ const structuralStep = contract?.steps.find((step) => step.structural_edit_plan_required === true);
620
+ if (structuralStep) {
621
+ mutationLanePolicy = {
622
+ mutation_lane: "structural_edit",
623
+ waiver: structuralStep.structural_edit_waiver ?? null,
624
+ };
625
+ }
626
+ }
609
627
  }
610
628
  // ── End plan validation preflight ────────────────────────────────────
611
629
  setSessionPhase(sessionId, "workspace_before_run");
@@ -706,7 +724,7 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
706
724
  const turnStartedAt = new Date().toISOString();
707
725
  const paths = buildTurnPaths(sessionId, sessionSnapshot.workspace_path, turnNumber);
708
726
  // Build capability-filtered tool catalog in-memory from the resolved model class.
709
- const filteredCatalog = buildFilteredToolCatalog(effectiveModelClass);
727
+ const filteredCatalog = buildFilteredToolCatalog(effectiveModelClass, undefined, mutationLanePolicy);
710
728
  const capabilitySnapshotId = randomUUID();
711
729
  const selectedBudget = resolveEffectiveSurgicalReadBudget(runtimeProfileSnapshot.profile, effectiveModelClass);
712
730
  const toolCatalog = filteredCatalog.entries.map((e) => ({
@@ -1078,6 +1096,31 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
1078
1096
  for (const toolCall of response.tool_calls) {
1079
1097
  const toolStartedAt = new Date().toISOString();
1080
1098
  noteProgress(`Runtime tool ${toolCall.name} started during turn ${turnNumber}.`);
1099
+ const fencedReason = filteredCatalog.unavailable_tools.find((entry) => entry.name === toolCall.name && entry.reason_code === "structural_lane_fenced");
1100
+ if (fencedReason) {
1101
+ const toolResult = {
1102
+ ok: false,
1103
+ name: toolCall.name,
1104
+ summary: fencedReason.reason,
1105
+ request_path: "policy://structural-lane-fenced/request",
1106
+ response_path: "policy://structural-lane-fenced/response",
1107
+ error: { reason_code: "structural_lane_fenced", summary: fencedReason.reason },
1108
+ };
1109
+ await appendStatusEventSafe({
1110
+ source_module: "capability-framework",
1111
+ event_type: "STRUCTURAL_LANE_FENCED",
1112
+ status: "blocked",
1113
+ summary: fencedReason.reason,
1114
+ payload: {
1115
+ session_id: sessionId,
1116
+ turn_number: turnNumber,
1117
+ tool_name: toolCall.name,
1118
+ reason_code: "structural_lane_fenced",
1119
+ },
1120
+ }).catch(() => undefined);
1121
+ toolCalls.push(mapToolCallResult(toolResult, toolStartedAt));
1122
+ continue;
1123
+ }
1081
1124
  const toolResult = await executeRuntimeTool(toolCall.name, toolCall.input, {
1082
1125
  session_id: sessionId,
1083
1126
  workspace_path: sessionSnapshot.workspace_path,
@@ -102,6 +102,13 @@ export interface FilteredToolCatalog {
102
102
  }[];
103
103
  tool_cost_class: Record<string, ToolCostClass>;
104
104
  }
105
- export declare function buildFilteredToolCatalog(modelClass: ModelClass | undefined, allowedTools?: string[]): FilteredToolCatalog;
105
+ export interface MutationLanePolicy {
106
+ mutation_lane: "structural_edit" | "raw_write_allowed";
107
+ waiver?: {
108
+ reason: string;
109
+ evidence_ref: string;
110
+ } | null;
111
+ }
112
+ export declare function buildFilteredToolCatalog(modelClass: ModelClass | undefined, allowedTools?: string[], lanePolicy?: MutationLanePolicy): FilteredToolCatalog;
106
113
  export {};
107
114
  //# sourceMappingURL=runtime-tool-specs.d.ts.map
@@ -594,7 +594,12 @@ function extractCostClass(tool) {
594
594
  return "moderate";
595
595
  return "cheap";
596
596
  }
597
- export function buildFilteredToolCatalog(modelClass, allowedTools) {
597
+ const STRUCTURAL_LANE_FENCED = new Set([
598
+ "write_workspace_file",
599
+ "safe_edit_file",
600
+ "apply_patch",
601
+ ]);
602
+ export function buildFilteredToolCatalog(modelClass, allowedTools, lanePolicy) {
598
603
  const allTools = listRuntimeToolSpecs();
599
604
  const effectiveModelClass = modelClass ?? "frontier";
600
605
  const allowed = allowedTools ? new Set(allowedTools) : null;
@@ -621,6 +626,19 @@ export function buildFilteredToolCatalog(modelClass, allowedTools) {
621
626
  cost_class: costClass,
622
627
  });
623
628
  }
629
+ if (lanePolicy?.mutation_lane === "structural_edit"
630
+ && !(lanePolicy.waiver?.reason?.trim() && lanePolicy.waiver?.evidence_ref?.trim())) {
631
+ entries = entries.filter((entry) => {
632
+ if (!STRUCTURAL_LANE_FENCED.has(entry.name))
633
+ return true;
634
+ unavailable.push({
635
+ name: entry.name,
636
+ reason: "Disabled by structural_edit lane. Use astgrep_locate -> compile_structural_edit -> preview_structural_edit -> astgrep_rewrite, or attach an evidence-backed structural_edit_waiver to the step.",
637
+ reason_code: "structural_lane_fenced",
638
+ });
639
+ return false;
640
+ });
641
+ }
624
642
  if (effectiveModelClass === "small_local" || effectiveModelClass === "mid") {
625
643
  const priority = new Map(SMALL_LOCAL_PRIORITY_TOOL_NAMES.map((name, idx) => [name, idx]));
626
644
  entries.sort((a, b) => {
@@ -7,6 +7,8 @@ export interface SafeEditInput {
7
7
  path: string;
8
8
  /** New file content. */
9
9
  content: string;
10
+ /** Optional full sha256:<hex> source precondition checked immediately before promotion. */
11
+ expected_base_hash?: string;
10
12
  /** Shell command to validate the change (e.g., "npx tsc --noEmit"). */
11
13
  validation_command?: string;
12
14
  /** Shell command to run tests (e.g., "npm test"). */
@@ -26,6 +28,11 @@ export interface SafeEditResult {
26
28
  test_passed?: boolean;
27
29
  test_output?: string;
28
30
  error?: string;
31
+ reason_code?: string;
32
+ expected_hash?: string;
33
+ observed_hash?: string;
34
+ lease_lock_path?: string;
35
+ lease_waited_ms?: number;
29
36
  restored?: boolean;
30
37
  }
31
38
  export interface TestSuiteResult {
@@ -43,6 +50,18 @@ export interface DiffResult {
43
50
  removed: number;
44
51
  diff_summary: string;
45
52
  }
53
+ export interface GcStagingInput {
54
+ max_age_hours?: number;
55
+ max_entries?: number;
56
+ }
57
+ export interface GcStagingResult {
58
+ ok: boolean;
59
+ staging_path: string;
60
+ removed_entries: number;
61
+ kept_entries: number;
62
+ errors: string[];
63
+ }
64
+ export declare function gcStaging(input?: GcStagingInput): GcStagingResult;
46
65
  export declare function safeEditFile(input: SafeEditInput): SafeEditResult;
47
66
  export declare function runTestSuite(command?: string): TestSuiteResult;
48
67
  export declare function diffContents(original: string, updated: string): DiffResult;
@@ -52,6 +71,7 @@ export declare function diffFiles(pathA: string, pathB: string): DiffResult & {
52
71
  export interface ApplyPatchInput {
53
72
  path: string;
54
73
  patch: string;
74
+ expected_base_hash?: string;
55
75
  validation_command?: string;
56
76
  test_command?: string;
57
77
  }