@voybio/ace-swarm 0.2.4 → 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.
@@ -173,6 +173,20 @@ Confidence: <score>% (<label>)
173
173
 
174
174
  ---
175
175
 
176
+ ## Validation Surface
177
+
178
+ - Run this skill against at least one known-pass baseline and one known-fail fixture before trusting a promotion decision.
179
+ - Validation command example: execute the configured eval harness runner and confirm deterministic suite counts in `agent-state/EVAL_REPORT.md`.
180
+
181
+ ---
182
+
183
+ ## Portability Notes
184
+
185
+ - Keep this skill vendor-neutral: it should work across Codex, Claude, Cursor, and Antigravity runtimes.
186
+ - Avoid provider-specific assumptions in suite execution or report parsing; rely on ACE artifacts as the source of truth.
187
+
188
+ ---
189
+
176
190
  ## Anti-Patterns
177
191
 
178
192
  | Anti-Pattern | Correct Behavior |
@@ -132,6 +132,20 @@ Also use when:
132
132
 
133
133
  ---
134
134
 
135
+ ## Validation Surface
136
+
137
+ - Run this check with one valid and one intentionally broken handoff payload to verify deterministic PASS/FAIL behavior.
138
+ - Validation command example: execute the handoff lint path used by the runtime and confirm schema, route, and evidence checks all emit rule-level outcomes.
139
+
140
+ ---
141
+
142
+ ## Portability Notes
143
+
144
+ - Keep lint output schema and rule IDs stable so different clients can consume results uniformly.
145
+ - Do not depend on provider-specific behavior; handoff validation must rely only on ACE artifacts and schemas.
146
+
147
+ ---
148
+
135
149
  ## Anti-Patterns
136
150
 
137
151
  | Anti-Pattern | Correct Behavior |
@@ -156,6 +156,20 @@ No incident closure without ALL of:
156
156
 
157
157
  ---
158
158
 
159
+ ## Validation Surface
160
+
161
+ - Run an incident simulation with synthetic `GATE_FAILED` events and verify severity classification, owner assignment, and timeline reconstruction are deterministic.
162
+ - Validation command example: replay a known event stream and confirm `global-state/INCIDENTS.md` and `agent-state/INCIDENT_TIMELINE.md` contain matching evidence-linked rows.
163
+
164
+ ---
165
+
166
+ ## Portability Notes
167
+
168
+ - Incident lifecycle states and emitted event names must remain stable across clients.
169
+ - Keep the protocol provider-agnostic: all decisions should be derivable from ACE state artifacts and event logs.
170
+
171
+ ---
172
+
159
173
  ## Anti-Patterns
160
174
 
161
175
  | Anti-Pattern | Correct Behavior |
@@ -161,6 +161,20 @@ Generated: <ISO8601>
161
161
 
162
162
  ---
163
163
 
164
+ ## Validation Surface
165
+
166
+ - Run this skill on a fixture with known duplicates and contradictions, then verify the reconciliation report counts match expected values.
167
+ - Validation command example: execute memory curation and confirm no source logs are modified while `MEMORY_INDEX.md` updates with provenance links.
168
+
169
+ ---
170
+
171
+ ## Portability Notes
172
+
173
+ - Preserve schema and section labels so downstream consumers can parse curated memory artifacts consistently.
174
+ - Keep curation logic independent of model/provider specifics; evidence linkage is the portable contract.
175
+
176
+ ---
177
+
164
178
  ## Anti-Patterns
165
179
 
166
180
  | Anti-Pattern | Correct Behavior |
@@ -160,6 +160,20 @@ Reason: <one sentence>
160
160
 
161
161
  ---
162
162
 
163
+ ## Validation Surface
164
+
165
+ - Validate this skill with one known-pass and one known-fail release packet to confirm gate outcomes are deterministic.
166
+ - Validation command example: run release sentry checks and verify `RELEASE_DECISION.md` always includes explicit gate evidence rows.
167
+
168
+ ---
169
+
170
+ ## Portability Notes
171
+
172
+ - Keep decision enums and gate names stable so any client can consume release outputs consistently.
173
+ - Avoid provider-specific assumptions; release readiness must be computed from ACE artifacts only.
174
+
175
+ ---
176
+
163
177
  ## Anti-Patterns
164
178
 
165
179
  | Anti-Pattern | Correct Behavior |
@@ -158,6 +158,20 @@ Every risk in `RISKS.md` must contain these fields:
158
158
 
159
159
  ---
160
160
 
161
+ ## Validation Surface
162
+
163
+ - Run this skill against a fixture risk set with expected probability-impact scores and verify computed tiers match exactly.
164
+ - Validation command example: execute risk quantification and confirm HIGH/CRITICAL entries always include owner, mitigation, and verification condition.
165
+
166
+ ---
167
+
168
+ ## Portability Notes
169
+
170
+ - Keep scoring scales and tier thresholds explicit and stable across clients.
171
+ - Quantification must remain vendor-neutral and based on ACE state artifacts, not model-specific behavior.
172
+
173
+ ---
174
+
161
175
  ## Anti-Patterns
162
176
 
163
177
  | Anti-Pattern | Correct Behavior |
@@ -151,6 +151,20 @@ Use when:
151
151
 
152
152
  ---
153
153
 
154
+ ## Validation Surface
155
+
156
+ - Validate schema updates against both old and new sample payload fixtures before promoting contract changes.
157
+ - Validation command example: run JSON-schema validation for backward and forward compatibility samples and record results in `EVIDENCE_LOG.md`.
158
+
159
+ ---
160
+
161
+ ## Portability Notes
162
+
163
+ - Preserve stable schema IDs, version fields, and migration metadata so any ACE client can consume updates.
164
+ - Keep evolution rules provider-agnostic; compatibility decisions must be artifact-driven, not model-driven.
165
+
166
+ ---
167
+
154
168
  ## Anti-Patterns
155
169
 
156
170
  | Anti-Pattern | Correct Behavior |
@@ -161,6 +161,20 @@ Overall: <PASS | WARN | CRITICAL>
161
161
 
162
162
  ---
163
163
 
164
+ ## Validation Surface
165
+
166
+ - Run audits against fixtures with known contradictions to verify deterministic CRITICAL/WARN classification.
167
+ - Validation command example: execute the state audit flow and confirm every checklist row maps to a concrete pass/fail artifact reference.
168
+
169
+ ---
170
+
171
+ ## Portability Notes
172
+
173
+ - Keep report structure and checklist IDs stable so multiple clients can parse outputs consistently.
174
+ - State drift checks must remain independent of model/provider implementation details.
175
+
176
+ ---
177
+
164
178
  ## Anti-Patterns
165
179
 
166
180
  | Anti-Pattern | Correct Behavior |
@@ -2,6 +2,6 @@
2
2
  "id": "gate-correctness",
3
3
  "type": "executable",
4
4
  "invariant": "All required tests pass",
5
- "command": "cd ace-mcp-server && npm test --silent",
5
+ "command": "if [ -f ace-mcp-server/package.json ]; then cd ace-mcp-server && npm test --silent; elif [ -f package.json ]; then npm test --silent; else echo 'package.json not found for gate-correctness' && exit 1; fi",
6
6
  "evidence_requirement": "Command output snippet with exit code 0"
7
7
  }
package/dist/cli.js CHANGED
@@ -25,6 +25,7 @@ Usage:
25
25
  ace init [options] Bootstrap the ACE store into current workspace
26
26
  ace turnkey [options] Project minimal workspace bootstrap stubs from the ACE store
27
27
  ace doctor [options] Validate ACE runtime + MCP readiness
28
+ ace cache [options] Cache ACE artifacts into ace-state.ace and optionally clean projections
28
29
  ace mcp-config [options] Print global/client MCP config snippet(s) from store
29
30
  ace preconfig Write .mcp-config/ bundle for all supported clients to workspace root
30
31
  ace paths Show resolved package/workspace paths
@@ -53,6 +54,10 @@ Options for doctor:
53
54
  --ollama-url <url> Legacy alias for --base-url
54
55
  --scan Probe common local Ollama + llama.cpp endpoints when URL is unset
55
56
 
57
+ Options for cache:
58
+ --dry-run Preview what would be cached and cleaned (no writes/deletes)
59
+ --no-clean Keep workspace ACE artifacts after caching them into ace-state.ace
60
+
56
61
  Options for mcp-config:
57
62
  --client <name> codex|vscode|claude|cursor|antigravity
58
63
  --all Print all client snippets for optional global install
@@ -680,6 +685,26 @@ async function main() {
680
685
  console.log(`Store compacted: ${result.before} → ${result.after} bytes (saved ${savedKb}KB)`);
681
686
  return;
682
687
  }
688
+ if (command === "cache") {
689
+ const { cacheWorkspaceArtifacts } = await import("./store/cache-workspace.js");
690
+ const dryRun = args.includes("--dry-run");
691
+ const clean = !args.includes("--no-clean");
692
+ const result = await cacheWorkspaceArtifacts(WORKSPACE_ROOT, { dryRun, clean });
693
+ console.log("ACE cache complete");
694
+ console.log(`Workspace: ${WORKSPACE_ROOT}`);
695
+ console.log(`Store: ${result.storePath}`);
696
+ console.log(`Scanned files: ${result.scanned_files}`);
697
+ console.log(`Cached files: ${result.cached_files}`);
698
+ console.log(`Kept projected files: ${result.kept_projected_files}`);
699
+ console.log(`Skipped files: ${result.skipped_files}`);
700
+ console.log(`Removed files: ${result.removed_files}`);
701
+ if (result.warnings.length > 0) {
702
+ console.warn(`Warnings (${result.warnings.length}):`);
703
+ for (const warning of result.warnings)
704
+ console.warn(` ! ${warning}`);
705
+ }
706
+ return;
707
+ }
683
708
  if (command === "help" || command === "--help" || command === "-h") {
684
709
  printHelp();
685
710
  return;
package/dist/helpers.d.ts CHANGED
@@ -66,6 +66,7 @@ export interface BootstrapResult {
66
66
  skipped: string[];
67
67
  }
68
68
  export declare function mapAceWorkspaceRelativePath(path: string): string;
69
+ export declare function isProjectedAceWorkspacePath(filePath: string): boolean;
69
70
  export declare function acePath(...segments: string[]): string;
70
71
  export declare function wsPath(...segments: string[]): string;
71
72
  /** Normalize a path for validation: workspace-relative, forward slashes. */
@@ -79,6 +80,7 @@ export declare function fileExists(filePath: string): boolean;
79
80
  export declare function resolveWorkspaceWritePath(filePath: string): string;
80
81
  export declare function resolveWorkspaceReadPath(filePath: string): string;
81
82
  export declare function resolveWorkspaceArtifactPath(filePath: string, mode?: "read" | "write"): string;
83
+ export declare function resolveStoreFallbackKeysForPath(filePath: string): string[];
82
84
  export declare function classifyPathSource(path?: string): ArtifactSource;
83
85
  /** Safely read a workspace file, returning either text or a not-found/access message. */
84
86
  export declare function safeRead(filePath: string): string;
package/dist/helpers.js CHANGED
@@ -8,7 +8,7 @@ import { fileURLToPath } from "node:url";
8
8
  import { buildHostInstructionText } from "./ace-server-instructions.js";
9
9
  import { isInside, normalizeRelPath } from "./shared.js";
10
10
  import { getWorkspaceStorePath, listStoreKeysSync, parseVirtualStorePath, readStoreBlobSync, readVirtualStorePathSync, toVirtualStorePath, } from "./store/store-snapshot.js";
11
- import { isOperationalArtifactPath, operationalArtifactKey, writeStoreBlobSync, writeOperationalArtifactSync, } from "./store/store-artifacts.js";
11
+ import { isOperationalArtifactPath, operationalArtifactKey, writeStoreBlobSync, } from "./store/store-artifacts.js";
12
12
  import { buildProviderDoctorCommands, buildOpenAiCompatibleBaseUrl, defaultModelForProvider, normalizeLocalBaseUrl, normalizeProvider, } from "./tui/provider-discovery.js";
13
13
  export { isInside, isReadError, normalizeRelPath, looksLikeSwarmHandoffPath } from "./shared.js";
14
14
  const __filename = fileURLToPath(import.meta.url);
@@ -111,6 +111,14 @@ export const ACE_GITHUB_ROOT_REL = ".github";
111
111
  export const ACE_HOST_AGENTS_REL = "AGENTS.md";
112
112
  export const ACE_HOST_CLAUDE_REL = "CLAUDE.md";
113
113
  export const ACE_HOST_CURSOR_RULES_REL = ".cursorrules";
114
+ const ACE_PROJECTED_WORKSPACE_FILES = new Set([
115
+ `${ACE_TASKS_ROOT_REL}/todo.md`,
116
+ `${ACE_ROOT_REL}/ace-hook-context.json`,
117
+ `${ACE_ROOT_REL}/agent-state/job-queue.json`,
118
+ `${ACE_ROOT_REL}/agent-state/job-locks.json`,
119
+ `${ACE_ROOT_REL}/agent-state/scheduler-lease.json`,
120
+ `${ACE_ROOT_REL}/agent-state/context-snapshots/index.json`,
121
+ ]);
114
122
  const ACE_MANAGED_PREFIXES = [
115
123
  "agent-state",
116
124
  "global-state",
@@ -386,6 +394,10 @@ export function mapAceWorkspaceRelativePath(path) {
386
394
  }
387
395
  return normalized;
388
396
  }
397
+ export function isProjectedAceWorkspacePath(filePath) {
398
+ const canonical = mapAceWorkspaceRelativePath(normalizeRelPath(filePath));
399
+ return ACE_PROJECTED_WORKSPACE_FILES.has(canonical);
400
+ }
389
401
  export function acePath(...segments) {
390
402
  return resolve(currentWorkspaceRoot(), ACE_ROOT_REL, ...segments);
391
403
  }
@@ -532,6 +544,9 @@ function resolveStoreFallbackKeys(filePath) {
532
544
  const statePrefix = `${ACE_ROOT_REL}/agent-state/`;
533
545
  if (canonical.startsWith(statePrefix)) {
534
546
  const rel = canonical.slice(statePrefix.length);
547
+ if (!rel.startsWith("context-snapshots/")) {
548
+ keys.add(`state/artifacts/agent-state/${rel}`);
549
+ }
535
550
  const runtimeProjectionKey = {
536
551
  "job-queue.json": "state/artifacts/agent-state/job-queue.json",
537
552
  "job-locks.json": "state/artifacts/agent-state/job-locks.json",
@@ -549,6 +564,10 @@ function resolveStoreFallbackKeys(filePath) {
549
564
  if (rel)
550
565
  keys.add(`state/memory/context_snapshots/${rel}`);
551
566
  }
567
+ const skillsPrefix = `${ACE_SKILLS_ROOT_REL}/`;
568
+ if (canonical.startsWith(skillsPrefix)) {
569
+ keys.add(`knowledge/skills/${canonical.slice(skillsPrefix.length)}`);
570
+ }
552
571
  const tasksPrefix = `${ACE_TASKS_ROOT_REL}/`;
553
572
  if (canonical.startsWith(tasksPrefix)) {
554
573
  keys.add(`knowledge/tasks/${canonical.slice(tasksPrefix.length)}`);
@@ -584,6 +603,9 @@ function resolveStoreFallbackKeys(filePath) {
584
603
  }
585
604
  return [...keys];
586
605
  }
606
+ export function resolveStoreFallbackKeysForPath(filePath) {
607
+ return resolveStoreFallbackKeys(filePath);
608
+ }
587
609
  function readFirstAvailable(paths) {
588
610
  const found = firstExistingPath(paths);
589
611
  if (!found) {
@@ -631,15 +653,14 @@ export function safeWrite(filePath, content) {
631
653
  const normalized = normalizeRelPath(filePath);
632
654
  const canonical = mapAceWorkspaceRelativePath(normalized);
633
655
  const root = currentWorkspaceRoot();
634
- if (canonical.startsWith(`${ACE_ROOT_REL}/agent-state/`)) {
635
- const rel = canonical.slice(`${ACE_ROOT_REL}/`.length);
636
- if (isOperationalArtifactPath(rel) && existsSync(getWorkspaceStorePath(root))) {
637
- return writeOperationalArtifactSync(root, rel, content);
638
- }
639
- }
656
+ let storeVirtualPath;
640
657
  const storeKeys = resolveStoreFallbackKeys(filePath);
641
658
  if (storeKeys.length > 0 && existsSync(getWorkspaceStorePath(root))) {
642
- writeStoreBlobSync(root, storeKeys[0], content);
659
+ storeVirtualPath = writeStoreBlobSync(root, storeKeys[0], content);
660
+ const isAcePath = canonical === ACE_ROOT_REL || canonical.startsWith(`${ACE_ROOT_REL}/`);
661
+ if (isAcePath && !isProjectedAceWorkspacePath(canonical)) {
662
+ return storeVirtualPath;
663
+ }
643
664
  }
644
665
  const abs = resolveWorkspaceWritePath(filePath);
645
666
  mkdirSync(dirname(abs), { recursive: true });
@@ -0,0 +1,22 @@
1
+ export interface CacheWorkspaceOptions {
2
+ dryRun?: boolean;
3
+ clean?: boolean;
4
+ }
5
+ export interface CacheWorkspaceResult {
6
+ storePath: string;
7
+ dry_run: boolean;
8
+ clean: boolean;
9
+ scanned_files: number;
10
+ cached_files: number;
11
+ removed_files: number;
12
+ kept_projected_files: number;
13
+ skipped_files: number;
14
+ warnings: string[];
15
+ cached: Array<{
16
+ path: string;
17
+ key: string;
18
+ bytes: number;
19
+ }>;
20
+ }
21
+ export declare function cacheWorkspaceArtifacts(workspaceRoot: string, options?: CacheWorkspaceOptions): Promise<CacheWorkspaceResult>;
22
+ //# sourceMappingURL=cache-workspace.d.ts.map
@@ -0,0 +1,143 @@
1
+ import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, relative } from "node:path";
3
+ import { ACE_ROOT_REL, isProjectedAceWorkspacePath, mapAceWorkspaceRelativePath, resolveStoreFallbackKeysForPath, } from "../helpers.js";
4
+ import { normalizeRelPath } from "../shared.js";
5
+ import { writeStoreBlobsSync } from "./store-artifacts.js";
6
+ const SCAN_ROOTS_REL = [
7
+ "agent-state",
8
+ ".agents/ACE/agent-state",
9
+ ".agents/ACE/tasks",
10
+ ".agents/ACE/skills",
11
+ ];
12
+ function isInside(base, target) {
13
+ const rel = relative(base, target);
14
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
15
+ }
16
+ function collectFiles(root) {
17
+ const out = [];
18
+ if (!existsSync(root))
19
+ return out;
20
+ const walk = (dir) => {
21
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
22
+ const abs = join(dir, entry.name);
23
+ if (entry.isDirectory()) {
24
+ walk(abs);
25
+ continue;
26
+ }
27
+ if (entry.isFile()) {
28
+ out.push(abs);
29
+ }
30
+ }
31
+ };
32
+ walk(root);
33
+ return out;
34
+ }
35
+ function pruneEmptyParents(path, stopAt) {
36
+ let cursor = dirname(path);
37
+ while (isInside(stopAt, cursor) && cursor !== stopAt && existsSync(cursor)) {
38
+ const entries = readdirSync(cursor);
39
+ if (entries.length > 0)
40
+ break;
41
+ rmSync(cursor, { recursive: true, force: true });
42
+ cursor = dirname(cursor);
43
+ }
44
+ }
45
+ function isTextBuffer(input) {
46
+ return !input.includes(0);
47
+ }
48
+ function selectScanRoots(workspaceRoot) {
49
+ return SCAN_ROOTS_REL.map((relPath) => join(workspaceRoot, relPath)).filter((abs) => existsSync(abs));
50
+ }
51
+ export async function cacheWorkspaceArtifacts(workspaceRoot, options = {}) {
52
+ const dryRun = options.dryRun ?? false;
53
+ const clean = options.clean ?? true;
54
+ const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.ace");
55
+ if (!existsSync(storePath)) {
56
+ throw new Error(`No store found at ${storePath}. Run 'ace init' first.`);
57
+ }
58
+ const warnings = [];
59
+ const scanRoots = selectScanRoots(workspaceRoot);
60
+ const candidates = [];
61
+ let scanned = 0;
62
+ let keptProjected = 0;
63
+ let skipped = 0;
64
+ for (const scanRoot of scanRoots) {
65
+ for (const absPath of collectFiles(scanRoot)) {
66
+ scanned += 1;
67
+ const relPath = normalizeRelPath(relative(workspaceRoot, absPath));
68
+ const canonicalPath = mapAceWorkspaceRelativePath(relPath);
69
+ if (!canonicalPath.startsWith(`${ACE_ROOT_REL}/`)) {
70
+ skipped += 1;
71
+ continue;
72
+ }
73
+ if (canonicalPath === `${ACE_ROOT_REL}/ace-state.ace`) {
74
+ skipped += 1;
75
+ continue;
76
+ }
77
+ if (isProjectedAceWorkspacePath(canonicalPath)) {
78
+ keptProjected += 1;
79
+ continue;
80
+ }
81
+ const raw = readFileSync(absPath);
82
+ if (!isTextBuffer(raw)) {
83
+ skipped += 1;
84
+ warnings.push(`Skipped non-text artifact: ${canonicalPath}`);
85
+ continue;
86
+ }
87
+ const fallbackKeys = resolveStoreFallbackKeysForPath(canonicalPath);
88
+ if (fallbackKeys.length === 0) {
89
+ skipped += 1;
90
+ warnings.push(`No store fallback key for ${canonicalPath}`);
91
+ continue;
92
+ }
93
+ candidates.push({
94
+ absPath,
95
+ canonicalPath,
96
+ key: fallbackKeys[0],
97
+ content: raw.toString("utf-8"),
98
+ bytes: raw.byteLength,
99
+ });
100
+ }
101
+ }
102
+ const dedupByKey = new Map();
103
+ for (const candidate of candidates) {
104
+ dedupByKey.set(candidate.key, candidate);
105
+ }
106
+ const uniqueForWrite = [...dedupByKey.values()];
107
+ if (!dryRun && uniqueForWrite.length > 0) {
108
+ writeStoreBlobsSync(workspaceRoot, uniqueForWrite.map((candidate) => ({
109
+ key: candidate.key,
110
+ content: candidate.content,
111
+ })));
112
+ }
113
+ let removed = 0;
114
+ if (!dryRun && clean && candidates.length > 0) {
115
+ const removedPaths = new Set();
116
+ for (const candidate of candidates) {
117
+ if (removedPaths.has(candidate.absPath))
118
+ continue;
119
+ rmSync(candidate.absPath, { force: true });
120
+ removedPaths.add(candidate.absPath);
121
+ removed += 1;
122
+ const pruneRoot = scanRoots.find((root) => isInside(root, candidate.absPath)) ?? workspaceRoot;
123
+ pruneEmptyParents(candidate.absPath, pruneRoot);
124
+ }
125
+ }
126
+ return {
127
+ storePath,
128
+ dry_run: dryRun,
129
+ clean,
130
+ scanned_files: scanned,
131
+ cached_files: candidates.length,
132
+ removed_files: removed,
133
+ kept_projected_files: keptProjected,
134
+ skipped_files: skipped,
135
+ warnings,
136
+ cached: candidates.map((candidate) => ({
137
+ path: candidate.canonicalPath,
138
+ key: candidate.key,
139
+ bytes: candidate.bytes,
140
+ })),
141
+ };
142
+ }
143
+ //# sourceMappingURL=cache-workspace.js.map
@@ -26,11 +26,6 @@ export class ContextSnapshotMaterializer {
26
26
  if (!existsSync(dir))
27
27
  mkdirSync(dir, { recursive: true });
28
28
  const snapshots = await this.repo.listSnapshots();
29
- const retained = new Set(["index.json"]);
30
- for (const snapshot of snapshots) {
31
- retained.add(snapshot.file);
32
- writeText(join(dir, snapshot.file), `${JSON.stringify(snapshot.record, null, 2)}\n`);
33
- }
34
29
  writeText(join(dir, "index.json"), `${JSON.stringify({
35
30
  snapshots: snapshots.map(({ name, file, timestamp, summary }) => ({
36
31
  name,
@@ -42,7 +37,7 @@ export class ContextSnapshotMaterializer {
42
37
  for (const file of readdirSync(dir)) {
43
38
  if (!file.endsWith(".json"))
44
39
  continue;
45
- if (retained.has(file))
40
+ if (file === "index.json")
46
41
  continue;
47
42
  rmSync(join(dir, file), { force: true });
48
43
  }
@@ -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";
@@ -80,6 +80,17 @@ function appendUniqueNote(target, note) {
80
80
  target.push(note);
81
81
  }
82
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
+ }
83
94
  function buildDefaultOrchestratorAmendment(input) {
84
95
  if (input.result.status !== "completed" || input.step.role !== "coders") {
85
96
  return undefined;
@@ -687,13 +698,23 @@ export function registerAgentTools(server) {
687
698
  .optional()
688
699
  .describe("Optional workspace root override; defaults to the active workspace"),
689
700
  }, 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
- });
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());
697
718
  const sessionId = typeof extra?.sessionId === "string" ? extra.sessionId : undefined;
698
719
  const planSource = Array.isArray(steps) && steps.length > 0 ? "explicit_steps" : "orchestrator_default_step";
699
720
  const planSteps = Array.isArray(steps) && steps.length > 0
@@ -704,20 +725,27 @@ export function registerAgentTools(server) {
704
725
  steps: planSteps,
705
726
  execution_mode: execution_mode ?? "sequential",
706
727
  });
707
- const bridge = new ModelBridge(createDefaultModelBridgeClients(runtime));
728
+ const bridge = runtime
729
+ ? new ModelBridge(createDefaultModelBridgeClients(runtime))
730
+ : undefined;
708
731
  const fallbackHandoffPrefix = `LOCAL-${plan.plan_id}-`;
709
732
  const vericifyWarnings = [];
710
733
  const supervised = await superviseTaskPlan(plan, {
711
734
  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
- });
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);
721
749
  },
722
750
  async createHandoff({ step, plan: activePlan }) {
723
751
  const created = await executeAceInternalTool("create_handoff", {
@@ -756,13 +784,13 @@ export function registerAgentTools(server) {
756
784
  },
757
785
  async getVericifyContext() {
758
786
  return tryVericifyPacket(() => getVericifyContextPacket({
759
- workspaceRoot: runtime.workspaceRoot,
787
+ workspaceRoot: effectiveWorkspaceRoot,
760
788
  }), (message) => appendUniqueNote(vericifyWarnings, `Vericify context unavailable for ${plan.plan_id}: ${message}`));
761
789
  },
762
790
  async getVericifyDelta(since) {
763
791
  return tryVericifyPacket(() => getVericifyDelta({
764
792
  since,
765
- workspaceRoot: runtime.workspaceRoot,
793
+ workspaceRoot: effectiveWorkspaceRoot,
766
794
  }), (message) => appendUniqueNote(vericifyWarnings, `Vericify delta unavailable for ${plan.plan_id}: ${message}`));
767
795
  },
768
796
  async openCircuitBreaker(reason) {
@@ -818,7 +846,10 @@ export function registerAgentTools(server) {
818
846
  {
819
847
  type: "text",
820
848
  text: JSON.stringify({
821
- runtime,
849
+ runtime: runtime ?? null,
850
+ execution_backend: runtime ? "model_bridge" : "tool_only",
851
+ runtime_warnings: runtimeWarnings,
852
+ workspace_root: effectiveWorkspaceRoot,
822
853
  plan_source: planSource,
823
854
  planning_note: planSource === "orchestrator_default_step"
824
855
  ? "Auto-planning currently starts with ACE-Orchestrator. Pass explicit steps for multi-step orchestration."
@@ -4,7 +4,7 @@
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_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, 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
9
  import { defaultModelForProvider, } from "./tui/provider-discovery.js";
10
10
  import { refreshAstgrepIndex } from "./astgrep-index.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
  }
@@ -1349,31 +1408,27 @@ export function registerFrameworkTools(server) {
1349
1408
  .describe("Optional short focus string persisted with skeptic adversarial review evidence."),
1350
1409
  }, async ({ gate_ids, review_mode, review_focus }) => {
1351
1410
  const gatesDir = wsPath("agent-state", "MODULES", "gates");
1352
- if (!existsSync(gatesDir)) {
1353
- return {
1354
- content: [
1355
- {
1356
- type: "text",
1357
- text: "❌ Gates directory not found: agent-state/MODULES/gates/",
1358
- },
1359
- ],
1360
- };
1361
- }
1362
- 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/";
1363
1414
  // Filter to requested gates (or run all)
1364
1415
  const targets = gate_ids
1365
1416
  ? allGates.filter((g) => gate_ids.includes(g.id))
1366
1417
  : allGates;
1367
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(", ")}`;
1368
1422
  return {
1369
1423
  content: [
1370
1424
  {
1371
1425
  type: "text",
1372
- text: `❌ No matching gates found. Available: ${allGates.map((g) => g.id).join(", ")}`,
1426
+ text: noGateMessage,
1373
1427
  },
1374
1428
  ],
1375
1429
  };
1376
1430
  }
1431
+ const gateArtifactRefs = targets.map((gate) => gate.source_ref ?? gateEvidenceRef);
1377
1432
  const results = evaluateGateTargets(targets);
1378
1433
  const passed = results.filter((r) => r.ok).length;
1379
1434
  const failed = results.filter((r) => !r.ok).length;
@@ -1401,7 +1456,8 @@ export function registerFrameworkTools(server) {
1401
1456
  })),
1402
1457
  passed,
1403
1458
  failed,
1404
- evidence_ref: evidence?.evidenceRef ?? "agent-state/MODULES/gates/",
1459
+ evidence_ref: evidence?.evidenceRef ?? gateEvidenceRef,
1460
+ gate_manifest_source: resolved.source,
1405
1461
  ...(review
1406
1462
  ? {
1407
1463
  review: {
@@ -1428,13 +1484,14 @@ export function registerFrameworkTools(server) {
1428
1484
  ? `Gate execution with skeptic review: ${review.summary.confirmed_count} confirmed findings`
1429
1485
  : `Gate execution: ${passed}/${results.length} passed`,
1430
1486
  artifacts: [
1431
- ...results.map((r) => `agent-state/MODULES/gates/${r.id}.json`),
1487
+ ...gateArtifactRefs,
1432
1488
  ...(evidence ? ["agent-state/EVIDENCE_LOG.md"] : []),
1433
1489
  ],
1434
1490
  metadata: {
1435
1491
  passed,
1436
1492
  failed,
1437
1493
  gate_ids: results.map((r) => r.id),
1494
+ gate_manifest_source: resolved.source,
1438
1495
  ...(review
1439
1496
  ? {
1440
1497
  review_mode: review.mode,
@@ -1460,6 +1517,9 @@ export function registerFrameworkTools(server) {
1460
1517
  : allOk
1461
1518
  ? `✅ All gates passed: ${passed}/${results.length}`
1462
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/"}`,
1463
1523
  `Run ledger: ${ledger.path} (${ledger.entry.id})`,
1464
1524
  ...(evidence ? [`Evidence: ${evidence.path} (${evidence.evidenceRef})`] : []),
1465
1525
  "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voybio/ace-swarm",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "ACE Framework MCP server and CLI — single-file ACEPACK state, local-model serving, agent orchestration, and host compliance enforcement.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",