@voybio/ace-swarm 0.2.4 → 2.4.0

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 (125) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/README.md +20 -13
  3. package/assets/.agents/skills/eval-harness/SKILL.md +14 -0
  4. package/assets/.agents/skills/handoff-lint/SKILL.md +14 -0
  5. package/assets/.agents/skills/incident-commander/SKILL.md +14 -0
  6. package/assets/.agents/skills/memory-curator/SKILL.md +14 -0
  7. package/assets/.agents/skills/release-sentry/SKILL.md +14 -0
  8. package/assets/.agents/skills/risk-quant/SKILL.md +14 -0
  9. package/assets/.agents/skills/schema-forge/SKILL.md +14 -0
  10. package/assets/.agents/skills/state-auditor/SKILL.md +14 -0
  11. package/assets/agent-state/EVIDENCE_LOG.md +1 -1
  12. package/assets/agent-state/MODULES/gates/gate-correctness.json +1 -1
  13. package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
  14. package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
  15. package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
  16. package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
  17. package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
  18. package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
  19. package/assets/agent-state/STATUS.md +2 -2
  20. package/assets/scripts/ace-hook-dispatch.mjs +70 -6
  21. package/assets/scripts/render-mcp-configs.sh +19 -5
  22. package/dist/ace-context.js +22 -1
  23. package/dist/ace-server-instructions.js +3 -3
  24. package/dist/ace-state-resolver.js +5 -3
  25. package/dist/astgrep-index.d.ts +9 -1
  26. package/dist/astgrep-index.js +14 -3
  27. package/dist/cli.js +52 -20
  28. package/dist/handoff-registry.js +5 -5
  29. package/dist/helpers/artifacts.d.ts +19 -0
  30. package/dist/helpers/artifacts.js +152 -0
  31. package/dist/helpers/bootstrap.d.ts +24 -0
  32. package/dist/helpers/bootstrap.js +894 -0
  33. package/dist/helpers/constants.d.ts +53 -0
  34. package/dist/helpers/constants.js +288 -0
  35. package/dist/helpers/drift.d.ts +13 -0
  36. package/dist/helpers/drift.js +45 -0
  37. package/dist/helpers/path-utils.d.ts +17 -0
  38. package/dist/helpers/path-utils.js +104 -0
  39. package/dist/helpers/store-resolution.d.ts +19 -0
  40. package/dist/helpers/store-resolution.js +301 -0
  41. package/dist/helpers/workspace-root.d.ts +3 -0
  42. package/dist/helpers/workspace-root.js +80 -0
  43. package/dist/helpers.d.ts +8 -123
  44. package/dist/helpers.js +8 -1747
  45. package/dist/job-scheduler.js +3 -3
  46. package/dist/local-model-runtime.js +12 -1
  47. package/dist/model-bridge.d.ts +7 -0
  48. package/dist/model-bridge.js +75 -5
  49. package/dist/orchestrator-supervisor.d.ts +14 -0
  50. package/dist/orchestrator-supervisor.js +72 -1
  51. package/dist/run-ledger.js +3 -3
  52. package/dist/runtime-command.d.ts +8 -0
  53. package/dist/runtime-command.js +38 -6
  54. package/dist/runtime-executor.d.ts +14 -0
  55. package/dist/runtime-executor.js +669 -171
  56. package/dist/runtime-profile.d.ts +32 -0
  57. package/dist/runtime-profile.js +89 -13
  58. package/dist/runtime-tool-specs.d.ts +21 -0
  59. package/dist/runtime-tool-specs.js +78 -3
  60. package/dist/safe-edit.d.ts +7 -0
  61. package/dist/safe-edit.js +163 -37
  62. package/dist/schemas.js +19 -0
  63. package/dist/shared.d.ts +2 -2
  64. package/dist/status-events.js +9 -6
  65. package/dist/store/ace-packed-store.d.ts +3 -2
  66. package/dist/store/ace-packed-store.js +188 -110
  67. package/dist/store/bootstrap-store.d.ts +1 -1
  68. package/dist/store/bootstrap-store.js +94 -81
  69. package/dist/store/cache-workspace.d.ts +22 -0
  70. package/dist/store/cache-workspace.js +149 -0
  71. package/dist/store/materializers/context-snapshot-materializer.js +6 -7
  72. package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
  73. package/dist/store/materializers/hook-context-materializer.js +11 -21
  74. package/dist/store/materializers/host-file-materializer.js +6 -0
  75. package/dist/store/materializers/projection-manager.d.ts +0 -1
  76. package/dist/store/materializers/projection-manager.js +5 -13
  77. package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
  78. package/dist/store/materializers/vericify-projector.d.ts +7 -7
  79. package/dist/store/materializers/vericify-projector.js +11 -11
  80. package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
  81. package/dist/store/repositories/local-model-runtime-repository.js +242 -6
  82. package/dist/store/skills-install.d.ts +4 -0
  83. package/dist/store/skills-install.js +21 -12
  84. package/dist/store/state-reader.d.ts +2 -0
  85. package/dist/store/state-reader.js +20 -0
  86. package/dist/store/store-artifacts.d.ts +7 -0
  87. package/dist/store/store-artifacts.js +27 -1
  88. package/dist/store/store-authority-audit.d.ts +18 -1
  89. package/dist/store/store-authority-audit.js +115 -5
  90. package/dist/store/store-snapshot.d.ts +3 -0
  91. package/dist/store/store-snapshot.js +22 -2
  92. package/dist/store/workspace-store-paths.d.ts +39 -0
  93. package/dist/store/workspace-store-paths.js +94 -0
  94. package/dist/store/write-coordinator.d.ts +65 -0
  95. package/dist/store/write-coordinator.js +386 -0
  96. package/dist/todo-state.js +5 -5
  97. package/dist/tools-agent.js +319 -34
  98. package/dist/tools-discovery.js +1 -1
  99. package/dist/tools-files.d.ts +7 -0
  100. package/dist/tools-files.js +299 -10
  101. package/dist/tools-framework.js +107 -27
  102. package/dist/tools-handoff.js +2 -2
  103. package/dist/tools-lifecycle.js +4 -4
  104. package/dist/tools-memory.js +6 -6
  105. package/dist/tools-todo.js +2 -2
  106. package/dist/tracker-adapters.d.ts +1 -1
  107. package/dist/tracker-adapters.js +13 -18
  108. package/dist/tracker-sync.js +5 -3
  109. package/dist/tui/agent-runner.js +3 -1
  110. package/dist/tui/chat.js +103 -7
  111. package/dist/tui/dashboard.d.ts +1 -0
  112. package/dist/tui/dashboard.js +43 -0
  113. package/dist/tui/layout.d.ts +20 -0
  114. package/dist/tui/layout.js +31 -1
  115. package/dist/tui/local-model-contract.d.ts +6 -2
  116. package/dist/tui/local-model-contract.js +16 -3
  117. package/dist/vericify-bridge.d.ts +5 -0
  118. package/dist/vericify-bridge.js +27 -3
  119. package/dist/workspace-manager.d.ts +30 -3
  120. package/dist/workspace-manager.js +257 -27
  121. package/package.json +1 -2
  122. package/dist/internal-tool-runtime.d.ts +0 -21
  123. package/dist/internal-tool-runtime.js +0 -136
  124. package/dist/store/workspace-snapshot.d.ts +0 -26
  125. package/dist/store/workspace-snapshot.js +0 -107
@@ -2,16 +2,17 @@
2
2
  * Agent, skill, kernel, and task-pack tool registrations.
3
3
  */
4
4
  import { z } from "zod";
5
- import { ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, classifyPathSource, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, isSwarmRole, listAvailableSkills, readAgentInstructions, readAgentManifest, readKernelArtifact, readSkillInstructions, readTaskArtifact, resolveWritableTaskPath, safeWrite, } from "./helpers.js";
5
+ import { ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, classifyPathSource, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, isSwarmRole, listAvailableSkills, readAgentInstructions, readAgentManifest, readKernelArtifact, readSkillInstructions, readTaskArtifact, resolveWorkspaceRoot, resolveWritableTaskPath, safeWriteAsync, } from "./helpers.js";
6
6
  import { loadRuntimeProfile, readRuntimePromptTemplate, readRuntimeProfileState, validateRuntimeProfileContent, } from "./runtime-profile.js";
7
7
  import { getUnattendedSession, listUnattendedSessions, startUnattendedSession, stopUnattendedSession, validateRuntimeExecutorSessionRegistryContent, waitForUnattendedSession, } from "./runtime-executor.js";
8
8
  import { executeRuntimeTool, listRuntimeToolSpecs, loadRuntimeToolRegistry, validateRuntimeToolRegistryContent, } from "./runtime-tool-specs.js";
9
- import { createWorkspaceSession, listWorkspaceSessions, removeWorkspaceSession, resolveRuntimeWorkspaceRoot, validateManagedWorkspacePath, } from "./workspace-manager.js";
9
+ import { createWorkspaceSessionAsync, listWorkspaceSessions, removeWorkspaceSession, resolveRuntimeWorkspaceRoot, validateManagedWorkspacePath, } from "./workspace-manager.js";
10
10
  import { getTrackerAdapter, listTrackerAdapterKinds, loadTrackerSnapshot, validateTrackerSnapshotContent, } from "./tracker-adapters.js";
11
11
  import { refreshTrackerSnapshot } from "./tracker-sync.js";
12
12
  import { appendVericifyProcessPost, loadVericifyBridgeSnapshot, loadVericifyProcessPostLog, refreshVericifyBridgeSnapshot, validateVericifyBridgeSnapshotContent, validateVericifyProcessPostLogContent, } from "./vericify-bridge.js";
13
13
  import { getRoleTitle, ROLE_ENUM, KERNEL_KEY_ENUM, ROLE_TITLES } from "./shared.js";
14
14
  import { createDefaultModelBridgeClients, resolveLocalModelRuntime, runLocalModelTask, } from "./local-model-runtime.js";
15
+ import { withLocalModelRuntimeRepository } from "./store/repositories/local-model-runtime-repository.js";
15
16
  import { executeAceInternalTool } from "./ace-internal-tools.js";
16
17
  import { ModelBridge } from "./model-bridge.js";
17
18
  import { getVericifyContextPacket, getVericifyDelta } from "./vericify-context.js";
@@ -52,6 +53,11 @@ function extractToolTextContent(result) {
52
53
  .filter(Boolean)
53
54
  .join("\n");
54
55
  }
56
+ function didExecuteGatesPass(result) {
57
+ if (Boolean(result?.isError))
58
+ return false;
59
+ return !extractToolTextContent(result).trimStart().startsWith("❌");
60
+ }
55
61
  function mapRoleToTaskType(role) {
56
62
  switch (role) {
57
63
  case "vos":
@@ -71,15 +77,180 @@ function extractHandoffId(text) {
71
77
  const lineMatch = text.match(/handoff_id:\s*([A-Z0-9-]+)/i);
72
78
  return lineMatch?.[1];
73
79
  }
80
+ function stepLabel(index) {
81
+ return `step-${index + 1}`;
82
+ }
83
+ function isShipBoundaryTask(task) {
84
+ return /\b(ship|release|promot(?:e|ion)|land|merge)\b/i.test(task);
85
+ }
86
+ function isImplementationRole(role) {
87
+ return role === "coders" || role === "builder";
88
+ }
74
89
  async function buildOrchestratorSteps(task, sessionId) {
75
90
  void sessionId;
76
91
  return [{ role: "orchestrator", task }];
77
92
  }
93
+ function normalizeExplicitPlanSteps(steps, task) {
94
+ const originalIdByLabel = new Map();
95
+ const normalized = steps.map((step, index) => {
96
+ const id = `explicit-${index + 1}`;
97
+ originalIdByLabel.set(stepLabel(index), id);
98
+ return {
99
+ ...step,
100
+ id,
101
+ depends_on_ids: [...(step.depends_on ?? [])],
102
+ };
103
+ });
104
+ for (const step of normalized) {
105
+ step.depends_on_ids = step.depends_on_ids.map((dependency) => originalIdByLabel.get(dependency) ?? dependency);
106
+ }
107
+ let insertedResearch = false;
108
+ for (let index = 0; index < normalized.length; index += 1) {
109
+ const step = normalized[index];
110
+ if (step.role !== "spec")
111
+ continue;
112
+ const hasResearchBefore = normalized.slice(0, index).some((candidate) => candidate.role === "research");
113
+ if (hasResearchBefore)
114
+ continue;
115
+ const researchId = `auto-research-${index + 1}`;
116
+ normalized.splice(index, 0, {
117
+ id: researchId,
118
+ role: "research",
119
+ task: `Validate source-backed claims and gather evidence before spec work: ${step.task}`,
120
+ tool_scope: ["recall_context", "read_workspace_file", "build_continuity_packet"],
121
+ depends_on_ids: [],
122
+ });
123
+ step.depends_on_ids = Array.from(new Set([researchId, ...step.depends_on_ids]));
124
+ insertedResearch = true;
125
+ index += 1;
126
+ }
127
+ const shipFanoutEnabled = isShipBoundaryTask(task) || normalized.some((step) => isShipBoundaryTask(step.task));
128
+ if (shipFanoutEnabled) {
129
+ const hasReviewRole = normalized.some((step) => step.role === "skeptic");
130
+ const hasSecurityRole = normalized.some((step) => step.role === "security");
131
+ const hasQaRole = normalized.some((step) => step.role === "qa");
132
+ const hasReleaseRole = normalized.some((step) => step.role === "release");
133
+ const lastImplementation = [...normalized].reverse().find((step) => isImplementationRole(step.role)) ?? normalized.at(-1);
134
+ const implementationDependency = lastImplementation ? [lastImplementation.id] : [];
135
+ const fanoutStepIds = [];
136
+ if (!hasReviewRole) {
137
+ const reviewId = `auto-review-${normalized.length + 1}`;
138
+ normalized.push({
139
+ id: reviewId,
140
+ role: "skeptic",
141
+ task: `Review ship readiness for: ${task}`,
142
+ tool_scope: ["execute_gates", "validate_framework"],
143
+ depends_on_ids: implementationDependency,
144
+ });
145
+ fanoutStepIds.push(reviewId);
146
+ }
147
+ if (!hasSecurityRole) {
148
+ const securityId = `auto-security-${normalized.length + 1}`;
149
+ normalized.push({
150
+ id: securityId,
151
+ role: "security",
152
+ task: `Assess security readiness for: ${task}`,
153
+ tool_scope: ["execute_gates", "validate_framework"],
154
+ depends_on_ids: implementationDependency,
155
+ });
156
+ fanoutStepIds.push(securityId);
157
+ }
158
+ if (!hasQaRole) {
159
+ const qaId = `auto-qa-${normalized.length + 1}`;
160
+ normalized.push({
161
+ id: qaId,
162
+ role: "qa",
163
+ task: `Validate ship readiness for: ${task}`,
164
+ tool_scope: ["run_tests", "execute_gates", "git_diff"],
165
+ depends_on_ids: implementationDependency,
166
+ });
167
+ fanoutStepIds.push(qaId);
168
+ }
169
+ if (!hasReleaseRole && fanoutStepIds.length > 0) {
170
+ normalized.push({
171
+ id: `auto-release-${normalized.length + 1}`,
172
+ role: "release",
173
+ task: `Finalize ship decision for: ${task}`,
174
+ tool_scope: ["git_status", "execute_gates", "validate_framework"],
175
+ depends_on_ids: fanoutStepIds,
176
+ });
177
+ }
178
+ }
179
+ const finalIdByInternal = new Map();
180
+ normalized.forEach((step, index) => {
181
+ finalIdByInternal.set(step.id, stepLabel(index));
182
+ });
183
+ return {
184
+ steps: normalized.map((step) => ({
185
+ role: step.role,
186
+ task: step.task,
187
+ depends_on: step.depends_on_ids.length > 0
188
+ ? step.depends_on_ids.map((dependency) => finalIdByInternal.get(dependency) ?? dependency)
189
+ : undefined,
190
+ parallel_group: step.parallel_group,
191
+ tool_scope: step.tool_scope,
192
+ })),
193
+ insertedResearch,
194
+ shipFanoutEnabled,
195
+ };
196
+ }
197
+ async function normalizeOrchestratorPlanSteps(task, steps, sessionId) {
198
+ if (!Array.isArray(steps) || steps.length === 0) {
199
+ return {
200
+ planSource: "orchestrator_default_step",
201
+ normalization: {
202
+ steps: await buildOrchestratorSteps(task, sessionId),
203
+ insertedResearch: false,
204
+ shipFanoutEnabled: false,
205
+ },
206
+ };
207
+ }
208
+ return {
209
+ planSource: "explicit_steps",
210
+ normalization: normalizeExplicitPlanSteps(steps, task),
211
+ };
212
+ }
213
+ async function persistAcceptanceTraceMap(input) {
214
+ return safeWriteAsync("agent-state/ACCEPTANCE_TRACE_MAP.json", JSON.stringify({
215
+ version: 1,
216
+ generated_at: new Date().toISOString(),
217
+ plan_id: input.plan.plan_id,
218
+ task: input.task,
219
+ plan_source: input.planSource,
220
+ policies: {
221
+ inserted_research_before_spec: input.insertedResearch,
222
+ ship_fanout_enabled: input.shipFanoutEnabled,
223
+ },
224
+ steps: input.plan.steps.map((step) => ({
225
+ step_id: step.step_id,
226
+ role: step.role,
227
+ task: step.task,
228
+ depends_on: step.depends_on ?? [],
229
+ tool_scope: step.tool_scope ?? [],
230
+ verification_role: step.role === "coders" || step.role === "builder"
231
+ ? "qa"
232
+ : step.role === "spec"
233
+ ? "research"
234
+ : null,
235
+ })),
236
+ }, null, 2));
237
+ }
78
238
  function appendUniqueNote(target, note) {
79
239
  if (!target.includes(note)) {
80
240
  target.push(note);
81
241
  }
82
242
  }
243
+ function createToolOnlyBridgeResult(step, reason) {
244
+ return {
245
+ bridge_id: `tool-only-${step.step_id}-${Date.now()}`,
246
+ role: step.role,
247
+ status: "completed",
248
+ summary: `Tool-only orchestrator completion for ${step.step_id} (${step.role}): ${step.task}. ${reason}`,
249
+ turns: 0,
250
+ tool_calls: [],
251
+ child_results: [],
252
+ };
253
+ }
83
254
  function buildDefaultOrchestratorAmendment(input) {
84
255
  if (input.result.status !== "completed" || input.step.role !== "coders") {
85
256
  return undefined;
@@ -271,6 +442,45 @@ export function registerAgentTools(server) {
271
442
  ],
272
443
  };
273
444
  });
445
+ server.tool("get_transition_log", "Read the transition log for a session. Answers 'why stopped?', 'what changed?', and 'what evidence caused it?' from the ACE transition record store.", {
446
+ session_id: z.string().describe("Session ID to read transitions for"),
447
+ limit: z.number().int().positive().optional().default(20).describe("Max transitions to return (default: 20)"),
448
+ }, async ({ session_id, limit }) => {
449
+ try {
450
+ const transitions = await withLocalModelRuntimeRepository(resolveWorkspaceRoot(), (repo) => repo.getTransitionLog(session_id, limit ?? 20));
451
+ return {
452
+ content: [{ type: "text", text: JSON.stringify(transitions, null, 2) }],
453
+ };
454
+ }
455
+ catch (error) {
456
+ return {
457
+ content: [{ type: "text", text: `Error reading transition log: ${error instanceof Error ? error.message : String(error)}` }],
458
+ isError: true,
459
+ };
460
+ }
461
+ });
462
+ server.tool("get_capability_snapshot", "Read the capability snapshot for a specific turn. Shows which tools were available, their cost class, and any unavailable tools with reasons.", {
463
+ session_id: z.string().describe("Session ID"),
464
+ turn_number: z.number().int().positive().describe("Turn number"),
465
+ }, async ({ session_id, turn_number }) => {
466
+ try {
467
+ const snapshot = await withLocalModelRuntimeRepository(resolveWorkspaceRoot(), (repo) => repo.getCapabilitySnapshot(session_id, turn_number));
468
+ if (!snapshot) {
469
+ return {
470
+ content: [{ type: "text", text: `No capability snapshot found for session ${session_id} turn ${turn_number}` }],
471
+ };
472
+ }
473
+ return {
474
+ content: [{ type: "text", text: JSON.stringify(snapshot, null, 2) }],
475
+ };
476
+ }
477
+ catch (error) {
478
+ return {
479
+ content: [{ type: "text", text: `Error reading capability snapshot: ${error instanceof Error ? error.message : String(error)}` }],
480
+ isError: true,
481
+ };
482
+ }
483
+ });
274
484
  server.tool("validate_runtime_profile", "Validate ACE runtime profile markdown content or the current ACE_WORKFLOW.md file", {
275
485
  content: z
276
486
  .string()
@@ -482,7 +692,19 @@ export function registerAgentTools(server) {
482
692
  .boolean()
483
693
  .optional()
484
694
  .describe("If false, keep the managed workspace session after completion"),
485
- }, async ({ task, context_json, workspace_name, workspace_path, objective_id, tracker_item_id, max_turns, turn_timeout_ms, auto_cleanup, }) => {
695
+ emit_to: z
696
+ .array(z.enum(["tui", "tracker", "handoff", "vericify"]))
697
+ .optional()
698
+ .describe("Output emission targets for this session. Defaults to ['tui', 'vericify']."),
699
+ silent_unless_blocked: z
700
+ .boolean()
701
+ .optional()
702
+ .describe("If true, turns that produce no tool calls are classified as no_op_success and do not emit."),
703
+ require_approval_before_emit: z
704
+ .boolean()
705
+ .optional()
706
+ .describe("If true, require operator approval before emitting meaningful_completion output."),
707
+ }, async ({ task, context_json, workspace_name, workspace_path, objective_id, tracker_item_id, max_turns, turn_timeout_ms, auto_cleanup, emit_to, silent_unless_blocked, require_approval_before_emit, }) => {
486
708
  const result = await startUnattendedSession({
487
709
  task,
488
710
  context: parseOptionalJsonObject(context_json),
@@ -493,6 +715,9 @@ export function registerAgentTools(server) {
493
715
  max_turns,
494
716
  turn_timeout_ms,
495
717
  auto_cleanup,
718
+ emit_to,
719
+ silent_unless_blocked,
720
+ require_approval_before_emit,
496
721
  });
497
722
  return {
498
723
  content: [
@@ -687,37 +912,77 @@ export function registerAgentTools(server) {
687
912
  .optional()
688
913
  .describe("Optional workspace root override; defaults to the active workspace"),
689
914
  }, async ({ task, steps, execution_mode, max_turns_per_step, provider, model, base_url, ollama_url, workspace_root }, extra) => {
690
- const runtime = resolveLocalModelRuntime({
691
- workspaceRoot: workspace_root,
692
- provider,
693
- model,
694
- baseUrl: base_url,
695
- ollamaUrl: ollama_url,
696
- });
915
+ let runtime;
916
+ const runtimeWarnings = [];
917
+ try {
918
+ runtime = resolveLocalModelRuntime({
919
+ workspaceRoot: workspace_root,
920
+ provider,
921
+ model,
922
+ baseUrl: base_url,
923
+ ollamaUrl: ollama_url,
924
+ });
925
+ }
926
+ catch (error) {
927
+ const message = error instanceof Error ? error.message : String(error);
928
+ appendUniqueNote(runtimeWarnings, `Model bridge runtime unavailable; continuing in tool-only mode. ${message}`);
929
+ }
930
+ const effectiveWorkspaceRoot = runtime?.workspaceRoot ??
931
+ (workspace_root ? resolveRuntimeWorkspaceRoot(workspace_root) : resolveWorkspaceRoot());
697
932
  const sessionId = typeof extra?.sessionId === "string" ? extra.sessionId : undefined;
698
- const planSource = Array.isArray(steps) && steps.length > 0 ? "explicit_steps" : "orchestrator_default_step";
699
- const planSteps = Array.isArray(steps) && steps.length > 0
700
- ? steps
701
- : await buildOrchestratorSteps(task, sessionId);
933
+ const { planSource, normalization } = await normalizeOrchestratorPlanSteps(task, steps, sessionId);
702
934
  const plan = createTaskPlan({
703
935
  task,
704
- steps: planSteps,
936
+ steps: normalization.steps,
705
937
  execution_mode: execution_mode ?? "sequential",
706
938
  });
707
- const bridge = new ModelBridge(createDefaultModelBridgeClients(runtime));
939
+ const traceArtifactPath = await persistAcceptanceTraceMap({
940
+ plan,
941
+ task,
942
+ planSource,
943
+ insertedResearch: normalization.insertedResearch,
944
+ shipFanoutEnabled: normalization.shipFanoutEnabled,
945
+ });
946
+ const bridge = runtime
947
+ ? new ModelBridge(createDefaultModelBridgeClients(runtime))
948
+ : undefined;
708
949
  const fallbackHandoffPrefix = `LOCAL-${plan.plan_id}-`;
709
950
  const vericifyWarnings = [];
951
+ const appendSessionPlanTransition = async (stepId, from, to, reason, reasonCode) => {
952
+ if (!sessionId)
953
+ return;
954
+ await withLocalModelRuntimeRepository(effectiveWorkspaceRoot, async (repo) => {
955
+ const step = plan.steps.find((candidate) => candidate.step_id === stepId);
956
+ if (!step)
957
+ return;
958
+ await repo.appendTransitionRecord({
959
+ session_id: sessionId,
960
+ subject_kind: "plan_step",
961
+ subject_id: `${plan.plan_id}/${step.step_id}`,
962
+ from,
963
+ to,
964
+ reason,
965
+ reason_code: reasonCode,
966
+ evidence_refs: step.tool_scope ?? [],
967
+ });
968
+ }).catch(() => undefined);
969
+ };
710
970
  const supervised = await superviseTaskPlan(plan, {
711
971
  async spawnStep(step) {
712
- return bridge.spawn({
713
- task: step.task,
714
- role: step.role,
715
- workspace: runtime.workspaceRoot,
716
- maxTurns: max_turns_per_step ?? 6,
717
- provider: runtime.provider,
718
- model: runtime.model,
719
- toolScope: step.tool_scope,
720
- });
972
+ if (bridge && runtime) {
973
+ return bridge.spawn({
974
+ task: step.task,
975
+ role: step.role,
976
+ workspace: runtime.workspaceRoot,
977
+ maxTurns: max_turns_per_step ?? 6,
978
+ provider: runtime.provider,
979
+ model: runtime.model,
980
+ toolScope: step.tool_scope,
981
+ });
982
+ }
983
+ const reason = runtimeWarnings[0] ??
984
+ "No local model provider/runtime was resolved for model bridge execution.";
985
+ return createToolOnlyBridgeResult(step, reason);
721
986
  },
722
987
  async createHandoff({ step, plan: activePlan }) {
723
988
  const created = await executeAceInternalTool("create_handoff", {
@@ -747,7 +1012,12 @@ export function registerAgentTools(server) {
747
1012
  note,
748
1013
  }, sessionId);
749
1014
  },
750
- amendPlan({ plan: activePlan, step, result }) {
1015
+ async amendPlan({ plan: activePlan, step, result }) {
1016
+ await appendSessionPlanTransition(step.step_id, "running", step.status, `Plan step ${step.step_id} ${step.status}: ${result.summary}`, step.status === "done"
1017
+ ? "step_completed"
1018
+ : step.status === "blocked"
1019
+ ? "step_blocked"
1020
+ : "step_failed");
751
1021
  return buildDefaultOrchestratorAmendment({
752
1022
  plan: activePlan,
753
1023
  step,
@@ -756,13 +1026,13 @@ export function registerAgentTools(server) {
756
1026
  },
757
1027
  async getVericifyContext() {
758
1028
  return tryVericifyPacket(() => getVericifyContextPacket({
759
- workspaceRoot: runtime.workspaceRoot,
1029
+ workspaceRoot: effectiveWorkspaceRoot,
760
1030
  }), (message) => appendUniqueNote(vericifyWarnings, `Vericify context unavailable for ${plan.plan_id}: ${message}`));
761
1031
  },
762
1032
  async getVericifyDelta(since) {
763
1033
  return tryVericifyPacket(() => getVericifyDelta({
764
1034
  since,
765
- workspaceRoot: runtime.workspaceRoot,
1035
+ workspaceRoot: effectiveWorkspaceRoot,
766
1036
  }), (message) => appendUniqueNote(vericifyWarnings, `Vericify delta unavailable for ${plan.plan_id}: ${message}`));
767
1037
  },
768
1038
  async openCircuitBreaker(reason) {
@@ -779,7 +1049,7 @@ export function registerAgentTools(server) {
779
1049
  async executeGates() {
780
1050
  const result = await executeAceInternalTool("execute_gates", {}, sessionId);
781
1051
  return {
782
- ok: !Boolean(result?.isError),
1052
+ ok: didExecuteGatesPass(result),
783
1053
  summary: extractToolTextContent(result),
784
1054
  };
785
1055
  },
@@ -794,6 +1064,9 @@ export function registerAgentTools(server) {
794
1064
  }, sessionId);
795
1065
  },
796
1066
  async emitStatusEvent(event) {
1067
+ if (event.step_id) {
1068
+ await appendSessionPlanTransition(event.step_id, "planned", "running", `Plan step ${event.step_id} started: ${event.summary}`, "step_started");
1069
+ }
797
1070
  await executeAceInternalTool("emit_status_event", {
798
1071
  source_module: "capability-ops",
799
1072
  event_type: "ORCHESTRATOR_STEP",
@@ -818,11 +1091,23 @@ export function registerAgentTools(server) {
818
1091
  {
819
1092
  type: "text",
820
1093
  text: JSON.stringify({
821
- runtime,
1094
+ runtime: runtime ?? null,
1095
+ execution_backend: runtime ? "model_bridge" : "tool_only",
1096
+ runtime_warnings: runtimeWarnings,
1097
+ workspace_root: effectiveWorkspaceRoot,
822
1098
  plan_source: planSource,
823
1099
  planning_note: planSource === "orchestrator_default_step"
824
1100
  ? "Auto-planning currently starts with ACE-Orchestrator. Pass explicit steps for multi-step orchestration."
825
- : null,
1101
+ : normalization.insertedResearch
1102
+ ? "Research was inserted ahead of spec work to require source-backed evidence before specification."
1103
+ : normalization.shipFanoutEnabled
1104
+ ? "Ship fan-out enforcement added review, security, QA, and release coordination steps."
1105
+ : null,
1106
+ trace_artifact_path: traceArtifactPath,
1107
+ engskills_imports: {
1108
+ inserted_research_before_spec: normalization.insertedResearch,
1109
+ ship_fanout_enabled: normalization.shipFanoutEnabled,
1110
+ },
826
1111
  plan: supervised.plan,
827
1112
  step_summaries,
828
1113
  handoff_ids: supervised.handoff_ids,
@@ -1122,7 +1407,7 @@ export function registerAgentTools(server) {
1122
1407
  .optional()
1123
1408
  .describe("Optional hook timeout override in milliseconds"),
1124
1409
  }, async ({ workspace_name, workspace_path, source, objective_id, tracker_item_id, root, hooks_timeout_ms, }) => {
1125
- const result = createWorkspaceSession({
1410
+ const result = await createWorkspaceSessionAsync({
1126
1411
  workspace_name,
1127
1412
  workspace_path,
1128
1413
  source,
@@ -1173,7 +1458,7 @@ export function registerAgentTools(server) {
1173
1458
  .optional()
1174
1459
  .describe("Optional hook timeout override in milliseconds"),
1175
1460
  }, async ({ session_id, workspace_path, root, hooks_timeout_ms }) => {
1176
- const result = removeWorkspaceSession({
1461
+ const result = await removeWorkspaceSession({
1177
1462
  session_id,
1178
1463
  workspace_path,
1179
1464
  root,
@@ -1509,7 +1794,7 @@ export function registerAgentTools(server) {
1509
1794
  "",
1510
1795
  ].join("\n");
1511
1796
  const existing = readTaskArtifact("lessons");
1512
- const path = safeWrite(resolveWritableTaskPath("lessons"), `${existing}${entry}`);
1797
+ const path = await safeWriteAsync(resolveWritableTaskPath("lessons"), `${existing}${entry}`);
1513
1798
  return {
1514
1799
  content: [{ type: "text", text: `✅ Lesson recorded in ${path}` }],
1515
1800
  };
@@ -25,7 +25,7 @@ export function registerDiscoveryTools(server) {
25
25
  .optional()
26
26
  .describe("Include rep_astgrep.cxml corpus mining when available (default: true)"),
27
27
  }, async ({ scope, append_evidence, emit_event, include_rep_corpus }) => {
28
- const result = refreshAstgrepIndex({
28
+ const result = await refreshAstgrepIndex({
29
29
  scope,
30
30
  append_evidence,
31
31
  emit_event,
@@ -2,5 +2,12 @@
2
2
  * File operation tool registrations + new safe-edit and diff tools.
3
3
  */
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ export interface AstgrepRewriteTargetPlan {
6
+ ok: boolean;
7
+ affected_files: string[];
8
+ target_file?: string;
9
+ error?: string;
10
+ }
11
+ export declare function planAstgrepRewriteTargets(files: string[]): AstgrepRewriteTargetPlan;
5
12
  export declare function registerFileTools(server: McpServer): void;
6
13
  //# sourceMappingURL=tools-files.d.ts.map