@voybio/ace-swarm 0.2.3 → 0.2.5

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.
@@ -2,7 +2,7 @@
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, safeWrite, } 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";
@@ -52,25 +52,6 @@ function extractToolTextContent(result) {
52
52
  .filter(Boolean)
53
53
  .join("\n");
54
54
  }
55
- function normalizeRoleCandidate(input) {
56
- if (!input)
57
- return undefined;
58
- const normalized = input.trim().toLowerCase().replace(/^ace-/, "").replace(/[^a-z0-9]+/g, "");
59
- const candidate = normalized === "coder" ? "coders" : normalized;
60
- return ROLE_ENUM.options.includes(candidate)
61
- ? candidate
62
- : undefined;
63
- }
64
- function parseRoleFromRoutingSummary(summary) {
65
- const match = summary.match(/\*\*Primary Swarm Agent\(s\):\*\*\s*([^\n]+)/i);
66
- if (!match?.[1])
67
- return undefined;
68
- const firstAgent = match[1]
69
- .split(",")
70
- .map((entry) => entry.trim())
71
- .find(Boolean);
72
- return normalizeRoleCandidate(firstAgent);
73
- }
74
55
  function mapRoleToTaskType(role) {
75
56
  switch (role) {
76
57
  case "vos":
@@ -91,15 +72,25 @@ function extractHandoffId(text) {
91
72
  return lineMatch?.[1];
92
73
  }
93
74
  async function buildOrchestratorSteps(task, sessionId) {
94
- const routing = await executeAceInternalTool("route_task", { description: task, domain: "unknown" }, sessionId);
95
- const routedRole = parseRoleFromRoutingSummary(extractToolTextContent(routing)) ?? "orchestrator";
96
- return [{ role: routedRole, task }];
75
+ void sessionId;
76
+ return [{ role: "orchestrator", task }];
97
77
  }
98
78
  function appendUniqueNote(target, note) {
99
79
  if (!target.includes(note)) {
100
80
  target.push(note);
101
81
  }
102
82
  }
83
+ function createToolOnlyBridgeResult(step, reason) {
84
+ return {
85
+ bridge_id: `tool-only-${step.step_id}-${Date.now()}`,
86
+ role: step.role,
87
+ status: "completed",
88
+ summary: `Tool-only orchestrator completion for ${step.step_id} (${step.role}): ${step.task}. ${reason}`,
89
+ turns: 0,
90
+ tool_calls: [],
91
+ child_results: [],
92
+ };
93
+ }
103
94
  function buildDefaultOrchestratorAmendment(input) {
104
95
  if (input.result.status !== "completed" || input.step.role !== "coders") {
105
96
  return undefined;
@@ -566,9 +557,9 @@ export function registerAgentTools(server) {
566
557
  ],
567
558
  };
568
559
  });
569
- server.tool("run_local_model", "Offload a governed ACE subtask to a local model bridge and return the result", {
570
- task: z.string().describe("Task to execute with the local ACE model bridge"),
571
- role: ROLE_ENUM.optional().describe("Optional ACE role; defaults to route_task"),
560
+ server.tool("run_local_model", "Offload a governed ACE subtask to the provider-backed ACE bridge and return the result", {
561
+ task: z.string().describe("Task to execute with the ACE model bridge"),
562
+ role: ROLE_ENUM.optional().describe("Optional ACE role; defaults to orchestrator"),
572
563
  max_turns: z
573
564
  .number()
574
565
  .int()
@@ -582,15 +573,15 @@ export function registerAgentTools(server) {
582
573
  provider: z
583
574
  .string()
584
575
  .optional()
585
- .describe("Optional provider override; otherwise discovered from local runtime context"),
576
+ .describe("Optional provider override; otherwise discovered from workspace/runtime context"),
586
577
  model: z
587
578
  .string()
588
579
  .optional()
589
- .describe("Optional model override; otherwise discovered from local runtime context"),
580
+ .describe("Optional model override; otherwise discovered from workspace/runtime context"),
590
581
  base_url: z
591
582
  .string()
592
583
  .optional()
593
- .describe("Optional local runtime base URL override"),
584
+ .describe("Optional provider base URL override"),
594
585
  ollama_url: z
595
586
  .string()
596
587
  .optional()
@@ -621,7 +612,7 @@ export function registerAgentTools(server) {
621
612
  {
622
613
  type: "text",
623
614
  text: [
624
- "## Local Model Run",
615
+ "## Delegated Run",
625
616
  `- role: ${delegated.role}`,
626
617
  `- provider: ${delegated.runtime.provider}`,
627
618
  `- model: ${delegated.runtime.model}`,
@@ -655,7 +646,7 @@ export function registerAgentTools(server) {
655
646
  ],
656
647
  };
657
648
  });
658
- server.tool("run_orchestrator", "Execute a supervised plan via model bridge child runs; when steps are omitted, auto-planning falls back to a single route_task-derived step", {
649
+ server.tool("run_orchestrator", "Execute a supervised plan via model bridge child runs; when steps are omitted, the plan starts with ACE-Orchestrator", {
659
650
  task: z.string().describe("The task to decompose and execute"),
660
651
  steps: z
661
652
  .array(z.object({
@@ -675,7 +666,7 @@ export function registerAgentTools(server) {
675
666
  .describe("Optional ACE tool allowlist for the step"),
676
667
  }))
677
668
  .optional()
678
- .describe("Pre-defined steps; if omitted, the orchestrator derives a single routed step"),
669
+ .describe("Pre-defined steps; if omitted, the orchestrator starts with a single ACE-Orchestrator step"),
679
670
  execution_mode: z
680
671
  .enum(["sequential", "scheduled"])
681
672
  .optional()
@@ -689,15 +680,15 @@ export function registerAgentTools(server) {
689
680
  provider: z
690
681
  .string()
691
682
  .optional()
692
- .describe("Optional provider override; otherwise discovered from local runtime context"),
683
+ .describe("Optional provider override; otherwise discovered from workspace/runtime context"),
693
684
  model: z
694
685
  .string()
695
686
  .optional()
696
- .describe("Optional model override; otherwise discovered from local runtime context"),
687
+ .describe("Optional model override; otherwise discovered from workspace/runtime context"),
697
688
  base_url: z
698
689
  .string()
699
690
  .optional()
700
- .describe("Optional local runtime base URL override"),
691
+ .describe("Optional provider base URL override"),
701
692
  ollama_url: z
702
693
  .string()
703
694
  .optional()
@@ -707,15 +698,25 @@ export function registerAgentTools(server) {
707
698
  .optional()
708
699
  .describe("Optional workspace root override; defaults to the active workspace"),
709
700
  }, async ({ task, steps, execution_mode, max_turns_per_step, provider, model, base_url, ollama_url, workspace_root }, extra) => {
710
- const runtime = resolveLocalModelRuntime({
711
- workspaceRoot: workspace_root,
712
- provider,
713
- model,
714
- baseUrl: base_url,
715
- ollamaUrl: ollama_url,
716
- });
701
+ let runtime;
702
+ const runtimeWarnings = [];
703
+ try {
704
+ runtime = resolveLocalModelRuntime({
705
+ workspaceRoot: workspace_root,
706
+ provider,
707
+ model,
708
+ baseUrl: base_url,
709
+ ollamaUrl: ollama_url,
710
+ });
711
+ }
712
+ catch (error) {
713
+ const message = error instanceof Error ? error.message : String(error);
714
+ appendUniqueNote(runtimeWarnings, `Model bridge runtime unavailable; continuing in tool-only mode. ${message}`);
715
+ }
716
+ const effectiveWorkspaceRoot = runtime?.workspaceRoot ??
717
+ (workspace_root ? resolveRuntimeWorkspaceRoot(workspace_root) : resolveWorkspaceRoot());
717
718
  const sessionId = typeof extra?.sessionId === "string" ? extra.sessionId : undefined;
718
- const planSource = Array.isArray(steps) && steps.length > 0 ? "explicit_steps" : "route_task_single_step";
719
+ const planSource = Array.isArray(steps) && steps.length > 0 ? "explicit_steps" : "orchestrator_default_step";
719
720
  const planSteps = Array.isArray(steps) && steps.length > 0
720
721
  ? steps
721
722
  : await buildOrchestratorSteps(task, sessionId);
@@ -724,20 +725,27 @@ export function registerAgentTools(server) {
724
725
  steps: planSteps,
725
726
  execution_mode: execution_mode ?? "sequential",
726
727
  });
727
- const bridge = new ModelBridge(createDefaultModelBridgeClients(runtime));
728
+ const bridge = runtime
729
+ ? new ModelBridge(createDefaultModelBridgeClients(runtime))
730
+ : undefined;
728
731
  const fallbackHandoffPrefix = `LOCAL-${plan.plan_id}-`;
729
732
  const vericifyWarnings = [];
730
733
  const supervised = await superviseTaskPlan(plan, {
731
734
  async spawnStep(step) {
732
- return bridge.spawn({
733
- task: step.task,
734
- role: step.role,
735
- workspace: runtime.workspaceRoot,
736
- maxTurns: max_turns_per_step ?? 6,
737
- provider: runtime.provider,
738
- model: runtime.model,
739
- toolScope: step.tool_scope,
740
- });
735
+ if (bridge && runtime) {
736
+ return bridge.spawn({
737
+ task: step.task,
738
+ role: step.role,
739
+ workspace: runtime.workspaceRoot,
740
+ maxTurns: max_turns_per_step ?? 6,
741
+ provider: runtime.provider,
742
+ model: runtime.model,
743
+ toolScope: step.tool_scope,
744
+ });
745
+ }
746
+ const reason = runtimeWarnings[0] ??
747
+ "No local model provider/runtime was resolved for model bridge execution.";
748
+ return createToolOnlyBridgeResult(step, reason);
741
749
  },
742
750
  async createHandoff({ step, plan: activePlan }) {
743
751
  const created = await executeAceInternalTool("create_handoff", {
@@ -776,13 +784,13 @@ export function registerAgentTools(server) {
776
784
  },
777
785
  async getVericifyContext() {
778
786
  return tryVericifyPacket(() => getVericifyContextPacket({
779
- workspaceRoot: runtime.workspaceRoot,
787
+ workspaceRoot: effectiveWorkspaceRoot,
780
788
  }), (message) => appendUniqueNote(vericifyWarnings, `Vericify context unavailable for ${plan.plan_id}: ${message}`));
781
789
  },
782
790
  async getVericifyDelta(since) {
783
791
  return tryVericifyPacket(() => getVericifyDelta({
784
792
  since,
785
- workspaceRoot: runtime.workspaceRoot,
793
+ workspaceRoot: effectiveWorkspaceRoot,
786
794
  }), (message) => appendUniqueNote(vericifyWarnings, `Vericify delta unavailable for ${plan.plan_id}: ${message}`));
787
795
  },
788
796
  async openCircuitBreaker(reason) {
@@ -838,10 +846,13 @@ export function registerAgentTools(server) {
838
846
  {
839
847
  type: "text",
840
848
  text: JSON.stringify({
841
- runtime,
849
+ runtime: runtime ?? null,
850
+ execution_backend: runtime ? "model_bridge" : "tool_only",
851
+ runtime_warnings: runtimeWarnings,
852
+ workspace_root: effectiveWorkspaceRoot,
842
853
  plan_source: planSource,
843
- planning_note: planSource === "route_task_single_step"
844
- ? "Auto-planning currently falls back to a single route_task-derived step. Pass explicit steps for multi-step orchestration."
854
+ planning_note: planSource === "orchestrator_default_step"
855
+ ? "Auto-planning currently starts with ACE-Orchestrator. Pass explicit steps for multi-step orchestration."
845
856
  : null,
846
857
  plan: supervised.plan,
847
858
  step_summaries,
@@ -4,9 +4,9 @@
4
4
  */
5
5
  import { z } from "zod";
6
6
  import { bootstrapStoreWorkspace } from "./store/bootstrap-store.js";
7
- import { ACE_ROOT_REL, ACE_TASKS_ROOT_REL, ALL_MCP_CLIENTS, ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, WORKSPACE_ROOT, classifyPathSource, detectAssetDrift, getAllMcpServerConfigSnippets, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, getMcpClientInstallHint, getMcpServerConfigSnippet, getTaskArtifactPath, isSwarmRole, listAvailableSkills, normalizePathForValidation, safeRead, safeWrite, withFileLock, wsPath, } from "./helpers.js";
7
+ import { ACE_ROOT_REL, ACE_TASKS_ROOT_REL, ALL_MCP_CLIENTS, ALL_LLM_PROVIDERS, ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, WORKSPACE_ROOT, classifyPathSource, detectAssetDrift, getAllMcpServerConfigSnippets, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, getMcpClientInstallHint, getMcpServerConfigSnippet, getTaskArtifactPath, isSwarmRole, listAvailableSkills, normalizePathForValidation, resolveWorkspaceRoot, safeRead, safeWrite, withFileLock, wsPath, } from "./helpers.js";
8
8
  import { getRoleTitle, MCP_CLIENT_ENUM, scoreDomains, } from "./shared.js";
9
- import { DEFAULT_LLAMA_CPP_MODEL, DEFAULT_OLLAMA_MODEL, } from "./tui/provider-discovery.js";
9
+ import { defaultModelForProvider, } from "./tui/provider-discovery.js";
10
10
  import { refreshAstgrepIndex } from "./astgrep-index.js";
11
11
  import { scanWorkspaceDelta } from "./index-store.js";
12
12
  import { getOrRefreshKanbanSnapshot } from "./kanban.js";
@@ -20,6 +20,7 @@ import { auditPublicSurface } from "./public-surface.js";
20
20
  import { auditStoreAuthority, writeStoreAuthorityAuditReport, } from "./store/store-authority-audit.js";
21
21
  import { PROVENANCE_CRITICAL_EVENT_TYPES, validateArtifactManifestPayload, validateProvenanceLogContent, validateTealConfigContent, } from "./schemas.js";
22
22
  import { readAceTaskContractAssessment } from "./ace-autonomy.js";
23
+ import { listStoreKeysSync, readStoreBlobSync } from "./store/store-snapshot.js";
23
24
  function getArtifactManifestEntries(payload) {
24
25
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
25
26
  return [];
@@ -38,15 +39,37 @@ function getArtifactManifestEntries(payload) {
38
39
  }
39
40
  return [];
40
41
  }
42
+ function parseGateManifest(raw, sourceRef) {
43
+ try {
44
+ const gate = JSON.parse(raw);
45
+ const id = typeof gate.id === "string" ? gate.id.trim() : "";
46
+ if (!id)
47
+ return undefined;
48
+ const type = gate.type === "executable" || gate.type === "artifact_scan" || gate.type === "manual_review"
49
+ ? gate.type
50
+ : "manual_review";
51
+ return {
52
+ id,
53
+ type,
54
+ invariant: typeof gate.invariant === "string" ? gate.invariant : "",
55
+ command: typeof gate.command === "string" ? gate.command : "",
56
+ evidence_requirement: typeof gate.evidence_requirement === "string" ? gate.evidence_requirement : "",
57
+ source_ref: sourceRef,
58
+ };
59
+ }
60
+ catch {
61
+ return undefined;
62
+ }
63
+ }
41
64
  function readGateManifests(gatesDir) {
42
65
  const files = readdirSync(gatesDir).filter((f) => f.endsWith(".json"));
43
66
  const allGates = [];
44
67
  for (const file of files) {
45
68
  try {
46
69
  const raw = readFileSync(resolve(gatesDir, file), "utf-8");
47
- const gate = JSON.parse(raw);
48
- if (gate.id)
49
- allGates.push(gate);
70
+ const parsed = parseGateManifest(raw, `agent-state/MODULES/gates/${file}`);
71
+ if (parsed)
72
+ allGates.push(parsed);
50
73
  }
51
74
  catch {
52
75
  /* skip corrupt manifests */
@@ -54,6 +77,44 @@ function readGateManifests(gatesDir) {
54
77
  }
55
78
  return allGates;
56
79
  }
80
+ function readStoreGateManifests(workspaceRoot) {
81
+ const gateKeys = listStoreKeysSync(workspaceRoot, "knowledge/gates/").filter((key) => key.endsWith(".json"));
82
+ const allGates = [];
83
+ for (const key of gateKeys) {
84
+ const raw = readStoreBlobSync(workspaceRoot, key);
85
+ if (typeof raw !== "string")
86
+ continue;
87
+ const parsed = parseGateManifest(raw, key);
88
+ if (parsed)
89
+ allGates.push(parsed);
90
+ }
91
+ return allGates;
92
+ }
93
+ function resolveGateManifests(gatesDir) {
94
+ if (existsSync(gatesDir)) {
95
+ const workspaceGates = readGateManifests(gatesDir);
96
+ if (workspaceGates.length > 0) {
97
+ return { gates: workspaceGates, source: "workspace" };
98
+ }
99
+ }
100
+ const storeGates = readStoreGateManifests(resolveWorkspaceRoot());
101
+ if (storeGates.length > 0) {
102
+ return { gates: storeGates, source: "store" };
103
+ }
104
+ return { gates: [], source: "none" };
105
+ }
106
+ function hasArtifactEvidence(reference) {
107
+ const normalized = reference.trim();
108
+ if (!normalized)
109
+ return false;
110
+ const candidates = [wsPath("agent-state", normalized), wsPath(normalized)];
111
+ if (candidates.some((candidate) => existsSync(candidate)))
112
+ return true;
113
+ return candidates.some((candidate) => {
114
+ const content = safeRead(candidate);
115
+ return !content.startsWith("[FILE NOT FOUND]") && !content.startsWith("[ACCESS DENIED]");
116
+ });
117
+ }
57
118
  function evaluateGateTargets(targets) {
58
119
  const results = [];
59
120
  for (const gate of targets) {
@@ -97,7 +158,7 @@ function evaluateGateTargets(targets) {
97
158
  }
98
159
  else {
99
160
  for (const artifact of relevantArtifacts) {
100
- if (!existsSync(wsPath("agent-state", artifact))) {
161
+ if (!hasArtifactEvidence(artifact)) {
101
162
  missing.push(artifact);
102
163
  }
103
164
  }
@@ -109,11 +170,9 @@ function evaluateGateTargets(targets) {
109
170
  .map((s) => s.trim())
110
171
  .filter(Boolean);
111
172
  for (const ref of evidenceFiles) {
112
- const candidates = [wsPath("agent-state", ref), wsPath(ref)];
113
- const found = candidates.some((candidate) => existsSync(candidate));
114
- if (!found) {
173
+ if (!hasArtifactEvidence(ref)) {
115
174
  const asFile = ref.replace(/\s+/g, "_").replace(/[^a-zA-Z0-9_./-]/g, "");
116
- const fileFound = existsSync(wsPath("agent-state", asFile));
175
+ const fileFound = hasArtifactEvidence(asFile);
117
176
  if (!fileFound)
118
177
  missing.push(ref);
119
178
  }
@@ -289,7 +348,7 @@ export function registerFrameworkTools(server) {
289
348
  }
290
349
  const routingMap = {
291
350
  venture: {
292
- swarm_agents: ["vos"],
351
+ swarm_agents: ["orchestrator"],
293
352
  subagents: [
294
353
  "astgrep",
295
354
  "research",
@@ -298,11 +357,11 @@ export function registerFrameworkTools(server) {
298
357
  "skeptic",
299
358
  "ops",
300
359
  ],
301
- pipeline: "VOS -> UI -> Coders (as needed) with composable Research/Spec/Skeptic/Ops support",
302
- prompt: "ace-vos",
360
+ pipeline: "Orchestrator -> VOS/UI/Coders as needed with composable Research/Spec/Skeptic/Ops support",
361
+ prompt: "ace-orchestrator",
303
362
  },
304
363
  ux: {
305
- swarm_agents: ["ui"],
364
+ swarm_agents: ["orchestrator"],
306
365
  subagents: [
307
366
  "astgrep",
308
367
  "research",
@@ -313,11 +372,11 @@ export function registerFrameworkTools(server) {
313
372
  "skeptic",
314
373
  "ops",
315
374
  ],
316
- pipeline: "UI -> Coders with composable Spec/Builder/QA and Skeptic/Ops sidecars",
317
- prompt: "ace-ui",
375
+ pipeline: "Orchestrator -> UI/Coders with composable Spec/Builder/QA and Skeptic/Ops sidecars",
376
+ prompt: "ace-orchestrator",
318
377
  },
319
378
  engineering: {
320
- swarm_agents: ["coders"],
379
+ swarm_agents: ["orchestrator"],
321
380
  subagents: [
322
381
  "astgrep",
323
382
  "spec",
@@ -331,8 +390,8 @@ export function registerFrameworkTools(server) {
331
390
  "skeptic",
332
391
  "ops",
333
392
  ],
334
- pipeline: "Coders with composable Spec -> Builder -> QA -> Docs and Skeptic/Ops guards",
335
- prompt: "ace-coders",
393
+ pipeline: "Orchestrator -> Coders with composable Spec -> Builder -> QA -> Docs and Skeptic/Ops guards",
394
+ prompt: "ace-orchestrator",
336
395
  },
337
396
  mixed: {
338
397
  swarm_agents: ["orchestrator"],
@@ -486,7 +545,8 @@ export function registerFrameworkTools(server) {
486
545
  `**Primary Swarm Agent(s):** ${activeRoute.swarm_agents
487
546
  .map((agent) => `ACE-${getRoleTitle(agent)}`)
488
547
  .join(", ")}`,
489
- "**Hierarchy Rule:** Top-level routing stays locked to ACE-Orchestrator, ACE-VOS, ACE-UI, or ACE-Coders. Composable agents are delegated specialists, not peer replacements.",
548
+ "**Default Entry Agent:** ACE-Orchestrator",
549
+ "**Hierarchy Rule:** Top-level work starts with ACE-Orchestrator. VOS, UI, and Coders are delegated specialists, not peer replacements.",
490
550
  `**Preflight Owner:** ACE-Orchestrator`,
491
551
  `**Task Contract:** ${taskContract.ok ? "aligned" : "attention required"}`,
492
552
  `**Composable Subagents (Universal):** ${[...COMPOSABLE_AGENTS].join(", ")}`,
@@ -523,7 +583,7 @@ export function registerFrameworkTools(server) {
523
583
  };
524
584
  });
525
585
  // ── Bootstrap ─────────────────────────────────────────────────────
526
- server.tool("bootstrap_state", "Bootstrap ACE framework files (state, tasks, instructions, skills, scripts, MCP configs, optional local LLM profile)", {
586
+ server.tool("bootstrap_state", "Bootstrap ACE framework files (state, tasks, instructions, skills, scripts, MCP configs, optional runtime profile)", {
527
587
  project_name: z
528
588
  .string()
529
589
  .optional()
@@ -541,18 +601,28 @@ export function registerFrameworkTools(server) {
541
601
  .optional()
542
602
  .describe("Write minimal workspace host stubs (AGENTS.md, CLAUDE.md, .cursorrules, .github/copilot-instructions.md)"),
543
603
  llm_provider: z
544
- .enum(["ollama", "llama.cpp"])
604
+ .enum(ALL_LLM_PROVIDERS)
605
+ .optional()
606
+ .describe("Optional LLM runtime provider to scaffold"),
607
+ llm_model: z
608
+ .string()
609
+ .optional()
610
+ .describe("Model name for generated runtime profile"),
611
+ llm_base_url: z
612
+ .string()
545
613
  .optional()
546
- .describe("Optional local LLM profile provider to scaffold"),
614
+ .describe("Runtime base URL for generated provider profile"),
547
615
  ollama_model: z
548
616
  .string()
549
617
  .optional()
550
- .describe("Model name for generated local profile (legacy field name kept for compatibility)"),
618
+ .describe("Legacy alias for llm_model"),
551
619
  ollama_base_url: z
552
620
  .string()
553
621
  .optional()
554
- .describe("Local runtime base URL for generated profile (legacy field name kept for compatibility)"),
555
- }, async ({ project_name, force, include_mcp_config, include_client_config_bundle, llm_provider, ollama_model, ollama_base_url, }) => {
622
+ .describe("Legacy alias for llm_base_url"),
623
+ }, async ({ project_name, force, include_mcp_config, include_client_config_bundle, llm_provider, llm_model, llm_base_url, ollama_model, ollama_base_url, }) => {
624
+ const resolvedLlmModel = llm_model ?? ollama_model;
625
+ const resolvedLlmBaseUrl = llm_base_url ?? ollama_base_url;
556
626
  // Store-first bootstrap — initializes ace-state.ace, bakes host bundles into store,
557
627
  // and optionally materializes minimal workspace stubs.
558
628
  const storeResult = await bootstrapStoreWorkspace({
@@ -562,8 +632,8 @@ export function registerFrameworkTools(server) {
562
632
  includeMcpConfig: include_mcp_config,
563
633
  includeClientConfigBundle: include_client_config_bundle,
564
634
  llm: llm_provider ?? undefined,
565
- model: ollama_model ?? undefined,
566
- ollamaUrl: ollama_base_url ?? undefined,
635
+ model: resolvedLlmModel ?? undefined,
636
+ baseUrl: resolvedLlmBaseUrl ?? undefined,
567
637
  });
568
638
  const astIndex = refreshAstgrepIndex({
569
639
  scope: ".",
@@ -601,8 +671,8 @@ export function registerFrameworkTools(server) {
601
671
  materialized: storeResult.materialized.length,
602
672
  force: Boolean(force),
603
673
  llm_provider: llm_provider ?? null,
604
- llm_model: ollama_model ?? null,
605
- llm_base_url: ollama_base_url ?? null,
674
+ llm_model: resolvedLlmModel ?? null,
675
+ llm_base_url: resolvedLlmBaseUrl ?? null,
606
676
  indexed_files: delta.snapshot.file_count,
607
677
  index_truncated: delta.truncated,
608
678
  ast_index_scope: astIndex.scope,
@@ -645,10 +715,13 @@ export function registerFrameworkTools(server) {
645
715
  ...(llm_provider
646
716
  ? [
647
717
  "",
648
- "## Local LLM Profile",
718
+ "## LLM Runtime Profile",
649
719
  `- provider: ${llm_provider}`,
650
- `- model: ${ollama_model ?? (llm_provider === "llama.cpp" ? DEFAULT_LLAMA_CPP_MODEL : DEFAULT_OLLAMA_MODEL)}`,
651
- `- base_url: ${ollama_base_url ?? "discover via ace doctor --scan or set explicitly"}`,
720
+ `- model: ${resolvedLlmModel ?? defaultModelForProvider(llm_provider)}`,
721
+ `- base_url: ${resolvedLlmBaseUrl ??
722
+ (llm_provider === "ollama" || llm_provider === "llama.cpp"
723
+ ? "discover via ace doctor --scan or set explicitly"
724
+ : "optional; use provider defaults or set explicitly")}`,
652
725
  `- profile_path: ${storeResult.storePath}#state/runtime/llm_profile`,
653
726
  `- doctor_path: ${storeResult.storePath}#state/runtime/doctor_checks.md`,
654
727
  ]
@@ -1335,31 +1408,27 @@ export function registerFrameworkTools(server) {
1335
1408
  .describe("Optional short focus string persisted with skeptic adversarial review evidence."),
1336
1409
  }, async ({ gate_ids, review_mode, review_focus }) => {
1337
1410
  const gatesDir = wsPath("agent-state", "MODULES", "gates");
1338
- if (!existsSync(gatesDir)) {
1339
- return {
1340
- content: [
1341
- {
1342
- type: "text",
1343
- text: "❌ Gates directory not found: agent-state/MODULES/gates/",
1344
- },
1345
- ],
1346
- };
1347
- }
1348
- const allGates = readGateManifests(gatesDir);
1411
+ const resolved = resolveGateManifests(gatesDir);
1412
+ const allGates = resolved.gates;
1413
+ const gateEvidenceRef = resolved.source === "store" ? "knowledge/gates/*" : "agent-state/MODULES/gates/";
1349
1414
  // Filter to requested gates (or run all)
1350
1415
  const targets = gate_ids
1351
1416
  ? allGates.filter((g) => gate_ids.includes(g.id))
1352
1417
  : allGates;
1353
1418
  if (targets.length === 0) {
1419
+ const noGateMessage = allGates.length === 0
1420
+ ? "❌ No gate manifests found in agent-state/MODULES/gates/ or ace-state.ace knowledge/gates/*."
1421
+ : `❌ No matching gates found. Available: ${allGates.map((g) => g.id).join(", ")}`;
1354
1422
  return {
1355
1423
  content: [
1356
1424
  {
1357
1425
  type: "text",
1358
- text: `❌ No matching gates found. Available: ${allGates.map((g) => g.id).join(", ")}`,
1426
+ text: noGateMessage,
1359
1427
  },
1360
1428
  ],
1361
1429
  };
1362
1430
  }
1431
+ const gateArtifactRefs = targets.map((gate) => gate.source_ref ?? gateEvidenceRef);
1363
1432
  const results = evaluateGateTargets(targets);
1364
1433
  const passed = results.filter((r) => r.ok).length;
1365
1434
  const failed = results.filter((r) => !r.ok).length;
@@ -1387,7 +1456,8 @@ export function registerFrameworkTools(server) {
1387
1456
  })),
1388
1457
  passed,
1389
1458
  failed,
1390
- evidence_ref: evidence?.evidenceRef ?? "agent-state/MODULES/gates/",
1459
+ evidence_ref: evidence?.evidenceRef ?? gateEvidenceRef,
1460
+ gate_manifest_source: resolved.source,
1391
1461
  ...(review
1392
1462
  ? {
1393
1463
  review: {
@@ -1414,13 +1484,14 @@ export function registerFrameworkTools(server) {
1414
1484
  ? `Gate execution with skeptic review: ${review.summary.confirmed_count} confirmed findings`
1415
1485
  : `Gate execution: ${passed}/${results.length} passed`,
1416
1486
  artifacts: [
1417
- ...results.map((r) => `agent-state/MODULES/gates/${r.id}.json`),
1487
+ ...gateArtifactRefs,
1418
1488
  ...(evidence ? ["agent-state/EVIDENCE_LOG.md"] : []),
1419
1489
  ],
1420
1490
  metadata: {
1421
1491
  passed,
1422
1492
  failed,
1423
1493
  gate_ids: results.map((r) => r.id),
1494
+ gate_manifest_source: resolved.source,
1424
1495
  ...(review
1425
1496
  ? {
1426
1497
  review_mode: review.mode,
@@ -1446,6 +1517,9 @@ export function registerFrameworkTools(server) {
1446
1517
  : allOk
1447
1518
  ? `✅ All gates passed: ${passed}/${results.length}`
1448
1519
  : `❌ Gate failures: ${failed}/${results.length} failed`,
1520
+ `Gate manifest source: ${resolved.source === "store"
1521
+ ? "ace-state.ace (knowledge/gates/*)"
1522
+ : "agent-state/MODULES/gates/"}`,
1449
1523
  `Run ledger: ${ledger.path} (${ledger.entry.id})`,
1450
1524
  ...(evidence ? [`Evidence: ${evidence.path} (${evidence.evidenceRef})`] : []),
1451
1525
  "",
package/dist/tui/index.js CHANGED
@@ -17,7 +17,7 @@ import { OpenAICompatibleClient, diagnoseChatRuntimeConfig, } from "./openai-com
17
17
  import { detectColorLevel, write, cursor, screen, fg, style } from "./renderer.js";
18
18
  import { ALL_AGENTS, WORKSPACE_ROOT } from "../helpers.js";
19
19
  import { backfillHandoffsIntoScheduler } from "../tools-handoff.js";
20
- import { DEFAULT_OLLAMA_MODEL, inferProviderFromModel, normalizeLocalBaseUrl, } from "./provider-discovery.js";
20
+ import { DEFAULT_OLLAMA_MODEL, defaultModelForProvider, inferProviderFromModel, normalizeLocalBaseUrl, } from "./provider-discovery.js";
21
21
  import { resolveAceStateLayout } from "../ace-state-resolver.js";
22
22
  import { withLocalModelRuntimeRepository, } from "../store/repositories/local-model-runtime-repository.js";
23
23
  const DASHBOARD_CONTROLS = ["provider", "model", "chat", "logs", "refresh"];
@@ -56,7 +56,7 @@ export class AceTui {
56
56
  const workspaceRoot = options.workspaceRoot ?? WORKSPACE_ROOT;
57
57
  this.workspaceRoot = workspaceRoot;
58
58
  this.provider = this.normalizeProvider(options.provider ?? inferProviderFromModel(options.model) ?? "ollama") ?? "ollama";
59
- this.model = (options.model ?? DEFAULT_OLLAMA_MODEL).trim();
59
+ this.model = (options.model ?? defaultModelForProvider(this.provider)).trim();
60
60
  // Initialize modules
61
61
  const colorLevel = detectColorLevel();
62
62
  for (const [provider, baseUrl] of Object.entries(options.providerBaseUrls ?? {})) {
@@ -758,7 +758,7 @@ export class AceTui {
758
758
  this.model = DEFAULT_OLLAMA_MODEL;
759
759
  }
760
760
  else {
761
- this.model = `${this.provider}/default`;
761
+ this.model = defaultModelForProvider(this.provider);
762
762
  }
763
763
  this.config.set("model", this.model);
764
764
  this.telemetry.setModel(this.model);