@voybio/ace-swarm 0.2.0 → 0.2.1
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.
- package/README.md +15 -15
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/STATUS.md +2 -2
- package/dist/ace-autonomy.js +38 -1
- package/dist/ace-context.js +8 -0
- package/dist/ace-server-instructions.js +55 -19
- package/dist/ace-state-resolver.d.ts +18 -0
- package/dist/ace-state-resolver.js +106 -0
- package/dist/cli.js +67 -0
- package/dist/handoff-registry.js +11 -7
- package/dist/helpers.js +74 -8
- package/dist/job-scheduler.js +94 -44
- package/dist/run-ledger.js +3 -4
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/shared.d.ts +1 -1
- package/dist/status-events.js +12 -14
- package/dist/store/bootstrap-store.js +20 -9
- package/dist/store/materializers/context-snapshot-materializer.d.ts +10 -0
- package/dist/store/materializers/context-snapshot-materializer.js +51 -0
- package/dist/store/materializers/host-file-materializer.d.ts +6 -0
- package/dist/store/materializers/host-file-materializer.js +13 -0
- package/dist/store/materializers/projection-manager.d.ts +14 -0
- package/dist/store/materializers/projection-manager.js +73 -0
- package/dist/store/materializers/scheduler-projection-materializer.d.ts +16 -0
- package/dist/store/materializers/scheduler-projection-materializer.js +48 -0
- package/dist/store/repositories/context-snapshot-repository.d.ts +46 -0
- package/dist/store/repositories/context-snapshot-repository.js +105 -0
- package/dist/store/repositories/local-model-runtime-repository.d.ts +98 -0
- package/dist/store/repositories/local-model-runtime-repository.js +165 -0
- package/dist/store/repositories/scheduler-repository.d.ts +21 -39
- package/dist/store/repositories/scheduler-repository.js +123 -93
- package/dist/store/repositories/todo-repository.d.ts +4 -0
- package/dist/store/repositories/todo-repository.js +50 -0
- package/dist/store/state-reader.d.ts +8 -1
- package/dist/store/state-reader.js +12 -1
- package/dist/store/store-artifacts.js +31 -5
- package/dist/store/store-authority-audit.d.ts +30 -0
- package/dist/store/store-authority-audit.js +448 -0
- package/dist/store/types.d.ts +2 -0
- package/dist/store/types.js +1 -0
- package/dist/todo-state.js +179 -11
- package/dist/tools-files.js +2 -1
- package/dist/tools-framework.js +60 -0
- package/dist/tools-memory.js +69 -34
- package/dist/tools-todo.js +1 -1
- package/dist/tui/agent-worker.d.ts +1 -1
- package/dist/tui/agent-worker.js +5 -3
- package/dist/tui/chat.d.ts +19 -0
- package/dist/tui/chat.js +275 -9
- package/dist/tui/commands.d.ts +2 -0
- package/dist/tui/commands.js +62 -0
- package/dist/tui/dashboard.d.ts +5 -0
- package/dist/tui/dashboard.js +38 -2
- package/dist/tui/index.d.ts +5 -0
- package/dist/tui/index.js +146 -2
- package/dist/tui/input.js +5 -0
- package/dist/tui/layout.d.ts +24 -0
- package/dist/tui/layout.js +76 -2
- package/dist/tui/local-model-contract.d.ts +50 -0
- package/dist/tui/local-model-contract.js +272 -0
- package/dist/vericify-bridge.js +3 -4
- package/dist/vericify-context.js +18 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Autonomous Coding Entity (ACE) is a local MCP server and CLI for agent-assisted coding.
|
|
4
4
|
|
|
5
|
-
`ace-swarm`
|
|
5
|
+
`ace-swarm` provides Claude Code, Codex, Cursor, VS Code, and local-model workflows with a shared runtime: durable workspace state, typed handoffs, scheduler support, change intelligence, and operator tooling. All state lives in a single binary file — `.agents/ACE/ace-state.ace` — so the workspace keeps one source of truth instead of a forest of generated state files.
|
|
6
6
|
|
|
7
|
-
Modern coding agents can already read files, write code, run commands, and call tools. The missing layer is coordination that survives restarts, model switches, and multi-agent work. ACE
|
|
7
|
+
Modern coding agents can already read files, write code, run commands, and call tools. The missing layer is coordination that survives restarts, model switches, and multi-agent work. ACE keeps the workflow in the store and projects only the files external clients still need. For local models, ACE provides task context, tool surfaces, status events, evidence trails, and resumable state.
|
|
8
8
|
|
|
9
9
|
## ACE Manifesto
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ Modern coding agents can already read files, write code, run commands, and call
|
|
|
16
16
|
|
|
17
17
|
### Serving local models
|
|
18
18
|
|
|
19
|
-
ACE
|
|
19
|
+
ACE provides local models with the context they cannot infer on their own: task state, tool surfaces, status events, evidence trails, and the next move in the loop.
|
|
20
20
|
|
|
21
21
|
- `ace init --llm ollama` or `ace init --llm llama.cpp` records a local runtime profile in the store.
|
|
22
22
|
- `ace doctor` discovers the endpoint, checks the selected model, and keeps the runtime honest.
|
|
@@ -31,7 +31,9 @@ Frontier models are getting more capable and more restricted at the same time. P
|
|
|
31
31
|
- Ollama and llama.cpp keep local models in play.
|
|
32
32
|
- MCP remains the common protocol surface across clients and runtimes.
|
|
33
33
|
|
|
34
|
-
ACE sits underneath that stack. It does not compete with the host you already use. It
|
|
34
|
+
ACE sits underneath that stack. It does not compete with the host you already use. It provides that host with a durable local runtime.
|
|
35
|
+
|
|
36
|
+
ACE also provides scheduler primitives for queued work, dependency gates, leases, and resource locks so multi-agent runs stay coordinated.
|
|
35
37
|
|
|
36
38
|
## ACEPACK State Model
|
|
37
39
|
|
|
@@ -175,18 +177,16 @@ Developer or MCP host
|
|
|
175
177
|
+----------------------+--------------------------------------------------------------+
|
|
176
178
|
| Surface | Current scope |
|
|
177
179
|
+----------------------+--------------------------------------------------------------+
|
|
178
|
-
| Runtime roles |
|
|
179
|
-
| Skills |
|
|
180
|
-
| MCP server | stdio
|
|
181
|
-
| Durable state | single .ace authority for handoffs, status events,
|
|
182
|
-
|
|
|
183
|
-
|
|
|
184
|
-
| Coordination | priority dispatch, dependency gates, leases, resource locks |
|
|
180
|
+
| Runtime roles | swarm and composable runtime roles |
|
|
181
|
+
| Skills | packaged skills |
|
|
182
|
+
| MCP server | stdio surface for tools, prompts, resources, and workflows |
|
|
183
|
+
| Durable state | single .ace authority for handoffs, status events, todos, run ledger, job queue, and discovery |
|
|
184
|
+
| Projections | hook context, todo surface, and host config bundles |
|
|
185
|
+
| Coordination | scheduler queues, dependency gates, leases, and resource locks |
|
|
185
186
|
| Change intelligence | delta scan, semantic snapshots, drift reports, rewrite hints |
|
|
186
|
-
| Terminal UI | provider-aware TUI with chat, tabs, telemetry, agent
|
|
187
|
-
| Local models | Ollama and llama.cpp bootstrap, doctor checks,
|
|
188
|
-
|
|
|
189
|
-
| Verification | 242 tests passing in the current suite |
|
|
187
|
+
| Terminal UI | provider-aware TUI with chat, tabs, telemetry, and agent UI |
|
|
188
|
+
| Local models | Ollama and llama.cpp bootstrap, doctor checks, and bridges |
|
|
189
|
+
| Verification | automated test suite and regression coverage |
|
|
190
190
|
+----------------------+--------------------------------------------------------------+
|
|
191
191
|
```
|
|
192
192
|
|
|
@@ -4,4 +4,4 @@ Append-only chain-of-custody for checks, decisions, and execution proofs.
|
|
|
4
4
|
|
|
5
5
|
## Entries
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- {{BOOTSTRAP_TIMESTAMP}} | bootstrap initialized | evidence_ref: `EVIDENCE_LOG.md#ts:{{BOOTSTRAP_TIMESTAMP}}`
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# STATUS
|
|
2
2
|
|
|
3
|
-
- Current role: `
|
|
3
|
+
- Current role: `ace-orchestrator`
|
|
4
4
|
- Current phase: `STATE_ANALYSIS`
|
|
5
5
|
- Active pipeline: `standard`
|
|
6
6
|
- Blockers: `none`
|
|
7
7
|
- Current objective delta: `bootstrap complete, awaiting first scoped objective`
|
|
8
|
-
- Last update: `
|
|
8
|
+
- Last update: `{{BOOTSTRAP_TIMESTAMP}}`
|
package/dist/ace-autonomy.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { safeRead, wsPath } from "./helpers.js";
|
|
1
|
+
import { resolveWorkspaceRoot, safeRead, wsPath } from "./helpers.js";
|
|
2
2
|
import { readRunLedger } from "./run-ledger.js";
|
|
3
3
|
import { isReadError } from "./shared.js";
|
|
4
4
|
import { readStatusEvents } from "./status-events.js";
|
|
5
|
+
import { getWorkspaceStorePath, readStoreJsonSync, storeExistsSync, toVirtualStorePath, } from "./store/store-snapshot.js";
|
|
5
6
|
export const ACE_TASK_CONTRACT_FILES = [
|
|
6
7
|
"agent-state/TASK.md",
|
|
7
8
|
"agent-state/SCOPE.md",
|
|
@@ -102,6 +103,27 @@ function readStringArray(value) {
|
|
|
102
103
|
return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
103
104
|
}
|
|
104
105
|
function loadSnapshotIndex() {
|
|
106
|
+
const workspaceRoot = resolveWorkspaceRoot();
|
|
107
|
+
if (storeExistsSync(workspaceRoot)) {
|
|
108
|
+
const storeIndex = readStoreJsonSync(workspaceRoot, "state/memory/context_snapshots/index.json");
|
|
109
|
+
const snapshots = Array.isArray(storeIndex?.snapshots) ? storeIndex.snapshots : undefined;
|
|
110
|
+
if (snapshots) {
|
|
111
|
+
return {
|
|
112
|
+
snapshots: snapshots
|
|
113
|
+
.map((entry) => {
|
|
114
|
+
const item = coerceObject(entry);
|
|
115
|
+
const name = readString(item?.name);
|
|
116
|
+
const file = readString(item?.file);
|
|
117
|
+
const timestamp = readString(item?.timestamp);
|
|
118
|
+
const summary = readString(item?.summary);
|
|
119
|
+
if (!name || !file || !timestamp || !summary)
|
|
120
|
+
return undefined;
|
|
121
|
+
return { name, file, timestamp, summary };
|
|
122
|
+
})
|
|
123
|
+
.filter((entry) => Boolean(entry)),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
105
127
|
const raw = safeRead("agent-state/context-snapshots/index.json");
|
|
106
128
|
if (isReadError(raw))
|
|
107
129
|
return undefined;
|
|
@@ -240,6 +262,21 @@ export function readAceContextSnapshot(name) {
|
|
|
240
262
|
: index.snapshots[0];
|
|
241
263
|
if (!selected)
|
|
242
264
|
return undefined;
|
|
265
|
+
const workspaceRoot = resolveWorkspaceRoot();
|
|
266
|
+
if (storeExistsSync(workspaceRoot)) {
|
|
267
|
+
const storePath = getWorkspaceStorePath(workspaceRoot);
|
|
268
|
+
const key = `state/memory/context_snapshots/${selected.file}`;
|
|
269
|
+
const record = readStoreJsonSync(workspaceRoot, key);
|
|
270
|
+
if (record) {
|
|
271
|
+
return {
|
|
272
|
+
name: selected.name,
|
|
273
|
+
timestamp: selected.timestamp,
|
|
274
|
+
summary: selected.summary,
|
|
275
|
+
path: toVirtualStorePath(storePath, key),
|
|
276
|
+
content: clipText(JSON.stringify(record, null, 2), 1200),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
243
280
|
const relPath = wsPath("agent-state", "context-snapshots", selected.file);
|
|
244
281
|
const content = safeRead(relPath);
|
|
245
282
|
if (isReadError(content))
|
package/dist/ace-context.js
CHANGED
|
@@ -5,7 +5,9 @@ import { readRuntimeProfileState, renderRuntimePrompt } from "./runtime-profile.
|
|
|
5
5
|
const ROLE_TOOL_DEFAULTS = {
|
|
6
6
|
orchestrator: [
|
|
7
7
|
"recall_context",
|
|
8
|
+
"validate_framework",
|
|
8
9
|
"route_task",
|
|
10
|
+
"run_orchestrator",
|
|
9
11
|
"run_local_model",
|
|
10
12
|
"get_vericify_context",
|
|
11
13
|
"get_vericify_delta",
|
|
@@ -221,6 +223,12 @@ export function renderAceContext(options) {
|
|
|
221
223
|
"## Tool Scope",
|
|
222
224
|
toolCatalog || "- No ACE tools available.",
|
|
223
225
|
"",
|
|
226
|
+
"## Compact Guardrails",
|
|
227
|
+
"- Stop if you are about to claim tested, verified, or passed without evidence.",
|
|
228
|
+
"- If ACE state is thin or contradictory, call recall_context or validate_framework before improvising.",
|
|
229
|
+
"- For obviously multi-step work, prefer run_orchestrator over a single free-form answer.",
|
|
230
|
+
"- Do not declare completion while approval, retry, or blocker state remains active.",
|
|
231
|
+
"",
|
|
224
232
|
"## Output Contract",
|
|
225
233
|
'Respond in JSON only with one of these shapes:',
|
|
226
234
|
'- `{"status":"tool","thinking":"...","tool_calls":[{"tool":"name","input":{}}]}`',
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import { readAceLogicalFile, resolveAceLogicalPath, resolveAceStateLayout, } from "./ace-state-resolver.js";
|
|
3
4
|
const ACE_ROOT_REL = ".agents/ACE";
|
|
4
5
|
const CORE_FILES = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
"agent-state/TASK.md",
|
|
7
|
+
"agent-state/STATUS.md",
|
|
8
|
+
"agent-state/SCOPE.md",
|
|
9
|
+
"agent-state/EVIDENCE_LOG.md",
|
|
9
10
|
];
|
|
10
11
|
const STATE_DIRS = [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
"agent-state",
|
|
13
|
+
"global-state",
|
|
14
|
+
"venture-state",
|
|
15
|
+
"brand-state",
|
|
16
|
+
"engineering-state",
|
|
16
17
|
];
|
|
17
18
|
const BOOTSTRAP_TOOLS = new Set([
|
|
18
19
|
"bootstrap_state",
|
|
@@ -81,6 +82,17 @@ function truncate(input, max = 140) {
|
|
|
81
82
|
return `${input.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
|
|
82
83
|
}
|
|
83
84
|
function resolveWorkspacePath(workspaceRoot, relativePath) {
|
|
85
|
+
if (relativePath === "agent-state") {
|
|
86
|
+
const layout = resolveAceStateLayout(workspaceRoot);
|
|
87
|
+
if (layout.physicalMode === "top_level") {
|
|
88
|
+
return resolve(workspaceRoot, "agent-state");
|
|
89
|
+
}
|
|
90
|
+
return resolve(workspaceRoot, ACE_ROOT_REL, "agent-state");
|
|
91
|
+
}
|
|
92
|
+
if (relativePath.startsWith("agent-state/")) {
|
|
93
|
+
return (resolveAceLogicalPath(workspaceRoot, relativePath) ??
|
|
94
|
+
resolve(workspaceRoot, ACE_ROOT_REL, relativePath));
|
|
95
|
+
}
|
|
84
96
|
const canonicalRel = relativePath.startsWith(`${ACE_ROOT_REL}/`)
|
|
85
97
|
? relativePath
|
|
86
98
|
: `agent-state|global-state|venture-state|brand-state|engineering-state`
|
|
@@ -97,11 +109,33 @@ function resolveWorkspacePath(workspaceRoot, relativePath) {
|
|
|
97
109
|
}
|
|
98
110
|
return canonicalPath;
|
|
99
111
|
}
|
|
112
|
+
function displayWorkspacePath(workspaceRoot, relativePath) {
|
|
113
|
+
if (relativePath === "agent-state" || relativePath.startsWith("agent-state/")) {
|
|
114
|
+
return relativePath;
|
|
115
|
+
}
|
|
116
|
+
const canonicalRel = relativePath.startsWith(`${ACE_ROOT_REL}/`)
|
|
117
|
+
? relativePath
|
|
118
|
+
: `agent-state|global-state|venture-state|brand-state|engineering-state`
|
|
119
|
+
.split("|")
|
|
120
|
+
.some((prefix) => relativePath === prefix || relativePath.startsWith(`${prefix}/`))
|
|
121
|
+
? `${ACE_ROOT_REL}/${relativePath}`
|
|
122
|
+
: relativePath;
|
|
123
|
+
const canonicalPath = resolve(workspaceRoot, canonicalRel);
|
|
124
|
+
if (existsSync(canonicalPath)) {
|
|
125
|
+
return canonicalRel;
|
|
126
|
+
}
|
|
127
|
+
if (canonicalRel !== relativePath && existsSync(resolve(workspaceRoot, relativePath))) {
|
|
128
|
+
return relativePath;
|
|
129
|
+
}
|
|
130
|
+
return canonicalRel;
|
|
131
|
+
}
|
|
100
132
|
function listExistingCoreFiles(workspaceRoot) {
|
|
101
|
-
return CORE_FILES.filter((relativePath) =>
|
|
133
|
+
return CORE_FILES.filter((relativePath) => Boolean(resolveAceLogicalPath(workspaceRoot, relativePath)))
|
|
134
|
+
.map((relativePath) => displayWorkspacePath(workspaceRoot, relativePath));
|
|
102
135
|
}
|
|
103
136
|
function listExistingStateDirs(workspaceRoot) {
|
|
104
|
-
return STATE_DIRS.filter((relativePath) => existsSync(resolveWorkspacePath(workspaceRoot, relativePath)))
|
|
137
|
+
return STATE_DIRS.filter((relativePath) => existsSync(resolveWorkspacePath(workspaceRoot, relativePath)))
|
|
138
|
+
.map((relativePath) => displayWorkspacePath(workspaceRoot, relativePath));
|
|
105
139
|
}
|
|
106
140
|
function extractObjective(taskRaw) {
|
|
107
141
|
if (!taskRaw)
|
|
@@ -125,7 +159,7 @@ function extractStatusValue(statusRaw, label) {
|
|
|
125
159
|
return match?.[1] ? truncate(oneLine(match[1])) : undefined;
|
|
126
160
|
}
|
|
127
161
|
function extractAutonomyHint(workspaceRoot) {
|
|
128
|
-
const workflowRaw =
|
|
162
|
+
const workflowRaw = readAceLogicalFile(workspaceRoot, "agent-state/ACE_WORKFLOW.md");
|
|
129
163
|
if (!workflowRaw)
|
|
130
164
|
return undefined;
|
|
131
165
|
const hints = [];
|
|
@@ -142,8 +176,8 @@ function extractAutonomyHint(workspaceRoot) {
|
|
|
142
176
|
return hints.length > 0 ? hints.join(", ") : undefined;
|
|
143
177
|
}
|
|
144
178
|
export function buildAceDigest(workspaceRoot) {
|
|
145
|
-
const taskRaw =
|
|
146
|
-
const statusRaw =
|
|
179
|
+
const taskRaw = readAceLogicalFile(workspaceRoot, "agent-state/TASK.md");
|
|
180
|
+
const statusRaw = readAceLogicalFile(workspaceRoot, "agent-state/STATUS.md");
|
|
147
181
|
const objective = extractObjective(taskRaw);
|
|
148
182
|
const phase = extractStatusValue(statusRaw, "Current phase");
|
|
149
183
|
const blockers = extractStatusValue(statusRaw, "Blockers");
|
|
@@ -187,7 +221,7 @@ export function buildHostInstructionText(host, workspaceRoot) {
|
|
|
187
221
|
`# ACE Bootstrap For ${hostLabel}`,
|
|
188
222
|
"",
|
|
189
223
|
"This is a thin ACE bootstrap stub.",
|
|
190
|
-
"Full instructions, skills, task packs, and runtime state live
|
|
224
|
+
"Full instructions, skills, task packs, and runtime state live in ACE artifacts and should be accessed through the ACE MCP server.",
|
|
191
225
|
"",
|
|
192
226
|
"Session start protocol:",
|
|
193
227
|
"1. Call `recall_context` to load task, scope, and status from ACE state.",
|
|
@@ -195,7 +229,8 @@ export function buildHostInstructionText(host, workspaceRoot) {
|
|
|
195
229
|
"3. Prefer ACE prompts/resources (`ace-orchestrator`, `get_task_pack`, `get_skill_instructions`) over relying on this file.",
|
|
196
230
|
"",
|
|
197
231
|
"Ground truth:",
|
|
198
|
-
"-
|
|
232
|
+
"- `agent-state/*` is the logical state surface; the resolver reads it from top-level, nested, or store-backed layouts.",
|
|
233
|
+
"- `.agents/ACE/ace-state.ace` remains the canonical store-backed projection when that layout is used.",
|
|
199
234
|
"- `.agents/ACE/tasks/todo.md` is the human-facing todo surface.",
|
|
200
235
|
"- `.agents/ACE/ace-hook-context.json` is the compact hook snapshot.",
|
|
201
236
|
"",
|
|
@@ -216,9 +251,10 @@ export function buildBootstrapGuidance(workspaceRoot, toolName) {
|
|
|
216
251
|
return `ACE context loaded via \`${toolName}\`. Current digest: ${buildAceDigest(workspaceRoot)}.`;
|
|
217
252
|
}
|
|
218
253
|
export function buildMutationGuidance(workspaceRoot, toolName) {
|
|
254
|
+
const evidencePath = displayWorkspacePath(workspaceRoot, "agent-state/EVIDENCE_LOG.md");
|
|
219
255
|
return [
|
|
220
256
|
`ACE digest after \`${toolName}\`: ${buildAceDigest(workspaceRoot)}.`,
|
|
221
|
-
`If this changed code or state, reconcile \`${
|
|
257
|
+
`If this changed code or state, reconcile \`${evidencePath}\`, run \`execute_gates\` when appropriate, and use structured handoffs/process posts for delegated follow-up.`,
|
|
222
258
|
].join(" ");
|
|
223
259
|
}
|
|
224
260
|
export function buildRefreshGuidance(workspaceRoot, toolName) {
|
|
@@ -312,12 +348,12 @@ export function buildHookAdditionalContext(workspaceRoot) {
|
|
|
312
348
|
return parts.join("\n");
|
|
313
349
|
}
|
|
314
350
|
export function buildScopeReminder(workspaceRoot) {
|
|
315
|
-
const scopeRaw =
|
|
351
|
+
const scopeRaw = readAceLogicalFile(workspaceRoot, "agent-state/SCOPE.md");
|
|
316
352
|
if (!scopeRaw)
|
|
317
353
|
return undefined;
|
|
318
354
|
const scopeLines = nonEmptyLines(scopeRaw).filter((line) => !line.startsWith("#"));
|
|
319
355
|
if (scopeLines.length === 0) {
|
|
320
|
-
return `ACE scope reminder: review ${
|
|
356
|
+
return `ACE scope reminder: review ${displayWorkspacePath(workspaceRoot, "agent-state/SCOPE.md")} before mutating files or state.`;
|
|
321
357
|
}
|
|
322
358
|
return `ACE scope reminder: ${truncate(oneLine(scopeLines.slice(0, 4).join(" ")), 220)}`;
|
|
323
359
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type AcePhysicalMode = "top_level" | "nested_projection" | "store_projection" | "missing";
|
|
2
|
+
export interface AceStateResolution {
|
|
3
|
+
workspaceRoot: string;
|
|
4
|
+
logicalStateRoot: "agent-state";
|
|
5
|
+
physicalMode: AcePhysicalMode;
|
|
6
|
+
taskPath?: string;
|
|
7
|
+
statusPath?: string;
|
|
8
|
+
scopePath?: string;
|
|
9
|
+
evidencePath?: string;
|
|
10
|
+
storePath?: string;
|
|
11
|
+
hookSnapshotPath?: string;
|
|
12
|
+
isAcePresent: boolean;
|
|
13
|
+
missingCriticalArtifacts: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function readAceLogicalFile(workspaceRoot: string, relativePath: string): string | undefined;
|
|
16
|
+
export declare function resolveAceLogicalPath(workspaceRoot: string, relativePath: string): string | undefined;
|
|
17
|
+
export declare function resolveAceStateLayout(workspaceRoot: string): AceStateResolution;
|
|
18
|
+
//# sourceMappingURL=ace-state-resolver.d.ts.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { getWorkspaceStorePath, readStoreBlobSync, toVirtualStorePath } from "./store/store-snapshot.js";
|
|
4
|
+
import { isOperationalArtifactPath, operationalArtifactKey, } from "./store/store-artifacts.js";
|
|
5
|
+
const CRITICAL_LOGICAL_PATHS = [
|
|
6
|
+
"agent-state/TASK.md",
|
|
7
|
+
"agent-state/STATUS.md",
|
|
8
|
+
"agent-state/SCOPE.md",
|
|
9
|
+
];
|
|
10
|
+
const DISPLAY_LOGICAL_PATHS = [
|
|
11
|
+
"agent-state/TASK.md",
|
|
12
|
+
"agent-state/STATUS.md",
|
|
13
|
+
"agent-state/SCOPE.md",
|
|
14
|
+
"agent-state/EVIDENCE_LOG.md",
|
|
15
|
+
];
|
|
16
|
+
function workspacePath(root, relativePath) {
|
|
17
|
+
return resolve(root, relativePath);
|
|
18
|
+
}
|
|
19
|
+
function nestedAcePath(root, relativePath) {
|
|
20
|
+
return resolve(root, ".agents", "ACE", relativePath);
|
|
21
|
+
}
|
|
22
|
+
function readFileIfPresent(filePath) {
|
|
23
|
+
if (!existsSync(filePath))
|
|
24
|
+
return undefined;
|
|
25
|
+
try {
|
|
26
|
+
return readFileSync(filePath, "utf-8");
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function storeFallbackKeys(relativePath) {
|
|
33
|
+
const keys = [];
|
|
34
|
+
if (isOperationalArtifactPath(relativePath)) {
|
|
35
|
+
keys.push(operationalArtifactKey(relativePath));
|
|
36
|
+
}
|
|
37
|
+
keys.push(`knowledge/${relativePath}`);
|
|
38
|
+
return keys;
|
|
39
|
+
}
|
|
40
|
+
export function readAceLogicalFile(workspaceRoot, relativePath) {
|
|
41
|
+
const direct = readFileIfPresent(workspacePath(workspaceRoot, relativePath));
|
|
42
|
+
if (direct !== undefined)
|
|
43
|
+
return direct;
|
|
44
|
+
const nested = readFileIfPresent(nestedAcePath(workspaceRoot, relativePath));
|
|
45
|
+
if (nested !== undefined)
|
|
46
|
+
return nested;
|
|
47
|
+
for (const key of storeFallbackKeys(relativePath)) {
|
|
48
|
+
const storeValue = readStoreBlobSync(workspaceRoot, key);
|
|
49
|
+
if (typeof storeValue === "string")
|
|
50
|
+
return storeValue;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
export function resolveAceLogicalPath(workspaceRoot, relativePath) {
|
|
55
|
+
const direct = workspacePath(workspaceRoot, relativePath);
|
|
56
|
+
if (existsSync(direct))
|
|
57
|
+
return direct;
|
|
58
|
+
const nested = nestedAcePath(workspaceRoot, relativePath);
|
|
59
|
+
if (existsSync(nested))
|
|
60
|
+
return nested;
|
|
61
|
+
const storePath = getWorkspaceStorePath(workspaceRoot);
|
|
62
|
+
if (!existsSync(storePath))
|
|
63
|
+
return undefined;
|
|
64
|
+
for (const key of storeFallbackKeys(relativePath)) {
|
|
65
|
+
if (typeof readStoreBlobSync(workspaceRoot, key) === "string") {
|
|
66
|
+
return toVirtualStorePath(storePath, key);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
export function resolveAceStateLayout(workspaceRoot) {
|
|
72
|
+
const storePath = getWorkspaceStorePath(workspaceRoot);
|
|
73
|
+
const nestedStateDir = nestedAcePath(workspaceRoot, "agent-state");
|
|
74
|
+
const topLevelStateDir = workspacePath(workspaceRoot, "agent-state");
|
|
75
|
+
const topLevelPresent = DISPLAY_LOGICAL_PATHS.some((relativePath) => existsSync(workspacePath(workspaceRoot, relativePath)));
|
|
76
|
+
const nestedPresent = DISPLAY_LOGICAL_PATHS.some((relativePath) => existsSync(nestedAcePath(workspaceRoot, relativePath)));
|
|
77
|
+
const storePresent = existsSync(storePath);
|
|
78
|
+
let physicalMode = "missing";
|
|
79
|
+
if (topLevelPresent || existsSync(topLevelStateDir)) {
|
|
80
|
+
physicalMode = "top_level";
|
|
81
|
+
}
|
|
82
|
+
else if (nestedPresent || existsSync(nestedStateDir)) {
|
|
83
|
+
physicalMode = "nested_projection";
|
|
84
|
+
}
|
|
85
|
+
else if (storePresent) {
|
|
86
|
+
physicalMode = "store_projection";
|
|
87
|
+
}
|
|
88
|
+
const missingCriticalArtifacts = CRITICAL_LOGICAL_PATHS.filter((relativePath) => typeof readAceLogicalFile(workspaceRoot, relativePath) !== "string");
|
|
89
|
+
return {
|
|
90
|
+
workspaceRoot,
|
|
91
|
+
logicalStateRoot: "agent-state",
|
|
92
|
+
physicalMode,
|
|
93
|
+
taskPath: resolveAceLogicalPath(workspaceRoot, "agent-state/TASK.md"),
|
|
94
|
+
statusPath: resolveAceLogicalPath(workspaceRoot, "agent-state/STATUS.md"),
|
|
95
|
+
scopePath: resolveAceLogicalPath(workspaceRoot, "agent-state/SCOPE.md"),
|
|
96
|
+
evidencePath: resolveAceLogicalPath(workspaceRoot, "agent-state/EVIDENCE_LOG.md"),
|
|
97
|
+
storePath: storePresent ? storePath : undefined,
|
|
98
|
+
hookSnapshotPath: existsSync(nestedAcePath(workspaceRoot, "ace-hook-context.json"))
|
|
99
|
+
? nestedAcePath(workspaceRoot, "ace-hook-context.json")
|
|
100
|
+
: undefined,
|
|
101
|
+
isAcePresent: physicalMode !== "missing" ||
|
|
102
|
+
CRITICAL_LOGICAL_PATHS.some((relativePath) => typeof readAceLogicalFile(workspaceRoot, relativePath) === "string"),
|
|
103
|
+
missingCriticalArtifacts,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=ace-state-resolver.js.map
|
package/dist/cli.js
CHANGED
|
@@ -6,8 +6,10 @@ import { startStdioServer } from "./server.js";
|
|
|
6
6
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
7
7
|
import { waitForPendingStatusEventMirrors } from "./status-events.js";
|
|
8
8
|
import { bootstrapStoreWorkspace } from "./store/bootstrap-store.js";
|
|
9
|
+
import { HostFileMaterializer } from "./store/materializers/host-file-materializer.js";
|
|
9
10
|
import { openStore } from "./store/ace-packed-store.js";
|
|
10
11
|
import { withStoreWriteQueue } from "./store/write-queue.js";
|
|
12
|
+
import { DiscoveryRepository } from "./store/repositories/discovery-repository.js";
|
|
11
13
|
import { getWorkspaceStorePath, readStoreBlobSync, readStoreJsonSync, } from "./store/store-snapshot.js";
|
|
12
14
|
import { readFileSync } from "node:fs";
|
|
13
15
|
import { runTui } from "./tui/index.js";
|
|
@@ -23,6 +25,7 @@ Usage:
|
|
|
23
25
|
ace turnkey [options] Project minimal workspace bootstrap stubs from the ACE store
|
|
24
26
|
ace doctor [options] Validate local LLM + MCP readiness
|
|
25
27
|
ace mcp-config [options] Print global/client MCP config snippet(s) from store
|
|
28
|
+
ace preconfig Write .mcp-config/ bundle for all supported clients to workspace root
|
|
26
29
|
ace paths Show resolved package/workspace paths
|
|
27
30
|
ace help Show help
|
|
28
31
|
|
|
@@ -52,6 +55,9 @@ Options for doctor:
|
|
|
52
55
|
Options for mcp-config:
|
|
53
56
|
--client <name> codex|vscode|claude|cursor|antigravity
|
|
54
57
|
--all Print all client snippets for optional global install
|
|
58
|
+
|
|
59
|
+
preconfig writes .mcp-config/ at the workspace root with ready-to-use config files for every
|
|
60
|
+
supported MCP client. Run once after ace init. Each file includes the install hint for its client.
|
|
55
61
|
`);
|
|
56
62
|
}
|
|
57
63
|
function readFlagValue(args, flag) {
|
|
@@ -152,6 +158,33 @@ async function writeLlmProfile(profile) {
|
|
|
152
158
|
});
|
|
153
159
|
return `${storePath}#state/runtime/llm_profile`;
|
|
154
160
|
}
|
|
161
|
+
async function recordDiscoveryProfile(input) {
|
|
162
|
+
const storePath = getWorkspaceStorePath(WORKSPACE_ROOT);
|
|
163
|
+
await withStoreWriteQueue(storePath, async () => {
|
|
164
|
+
const store = await openStore(storePath);
|
|
165
|
+
try {
|
|
166
|
+
const discovery = new DiscoveryRepository(store);
|
|
167
|
+
await discovery.record({
|
|
168
|
+
provider: input.provider,
|
|
169
|
+
available: input.available,
|
|
170
|
+
endpoint: input.baseUrl,
|
|
171
|
+
models: input.models,
|
|
172
|
+
metadata: input.selectedModel
|
|
173
|
+
? {
|
|
174
|
+
profile_model: input.selectedModel,
|
|
175
|
+
runtime_kind: "local",
|
|
176
|
+
}
|
|
177
|
+
: {
|
|
178
|
+
runtime_kind: "local",
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
await store.commit();
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
await store.close();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
155
188
|
async function runInit(args, mode = "init") {
|
|
156
189
|
const projectName = readFlagValue(args, "--project");
|
|
157
190
|
const force = args.includes("--force");
|
|
@@ -400,6 +433,13 @@ async function runDoctor(args) {
|
|
|
400
433
|
});
|
|
401
434
|
}
|
|
402
435
|
const hasModel = modelNames.includes(model);
|
|
436
|
+
await recordDiscoveryProfile({
|
|
437
|
+
provider,
|
|
438
|
+
baseUrl,
|
|
439
|
+
models: modelNames,
|
|
440
|
+
selectedModel: model,
|
|
441
|
+
available: modelNames.length > 0,
|
|
442
|
+
});
|
|
403
443
|
checks.push({
|
|
404
444
|
name: provider === "ollama" ? "Requested model installed" : "Requested model served",
|
|
405
445
|
ok: hasModel,
|
|
@@ -437,6 +477,29 @@ function parseClientFlag(args) {
|
|
|
437
477
|
}
|
|
438
478
|
return match;
|
|
439
479
|
}
|
|
480
|
+
async function runPreconfig() {
|
|
481
|
+
const storePath = wsPath(".agents", "ACE", "ace-state.ace");
|
|
482
|
+
if (!fileExists(storePath)) {
|
|
483
|
+
console.error(`No ACE store found at ${storePath}. Run 'ace init' first.`);
|
|
484
|
+
process.exit(1);
|
|
485
|
+
}
|
|
486
|
+
const store = await openStore(storePath);
|
|
487
|
+
try {
|
|
488
|
+
const mat = new HostFileMaterializer(store, WORKSPACE_ROOT);
|
|
489
|
+
const written = await mat.materializeMcpBundle();
|
|
490
|
+
console.log(`ACE preconfig complete — wrote ${written.length} files to .mcp-config/\n`);
|
|
491
|
+
for (const p of written) {
|
|
492
|
+
console.log(` ${p}`);
|
|
493
|
+
}
|
|
494
|
+
console.log("\nInstall hints:");
|
|
495
|
+
for (const client of ALL_MCP_CLIENTS) {
|
|
496
|
+
console.log(` ${client}: ${getMcpClientInstallHint(client)}`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
finally {
|
|
500
|
+
await store.close();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
440
503
|
async function runMcpConfig(args) {
|
|
441
504
|
const all = args.includes("--all");
|
|
442
505
|
const explicitClient = parseClientFlag(args);
|
|
@@ -504,6 +567,10 @@ async function main() {
|
|
|
504
567
|
await runMcpConfig(args.slice(1));
|
|
505
568
|
return;
|
|
506
569
|
}
|
|
570
|
+
if (command === "preconfig") {
|
|
571
|
+
await runPreconfig();
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
507
574
|
if (command === "paths") {
|
|
508
575
|
console.log(`PACKAGE_ROOT=${PACKAGE_ROOT}`);
|
|
509
576
|
console.log(`DEFAULTS_ROOT=${DEFAULTS_ROOT}`);
|
package/dist/handoff-registry.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { resolveWorkspaceRoot, safeRead, safeWrite, wsPath, withFileLock } from "./helpers.js";
|
|
2
2
|
import { isReadError } from "./shared.js";
|
|
3
3
|
import { openStore } from "./store/ace-packed-store.js";
|
|
4
|
-
import {
|
|
4
|
+
import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
5
5
|
import { HandoffRepository } from "./store/repositories/handoff-repository.js";
|
|
6
6
|
import { getWorkspaceStorePath, listStoreKeysSync, readStoreJsonSync, storeExistsSync, } from "./store/store-snapshot.js";
|
|
7
7
|
import { withStoreWriteQueue } from "./store/write-queue.js";
|
|
8
|
+
import { waitForPendingStatusEventMirrors } from "./status-events.js";
|
|
9
|
+
import { waitForTodoStoreMirror } from "./todo-state.js";
|
|
8
10
|
const HANDOFF_REGISTRY_REL = "agent-state/handoff-registry.json";
|
|
9
11
|
function workspaceRoot() {
|
|
10
12
|
return resolveWorkspaceRoot();
|
|
@@ -291,6 +293,8 @@ export function getHandoffRegistryPath() {
|
|
|
291
293
|
export async function registerHandoffSafe(input) {
|
|
292
294
|
const root = workspaceRoot();
|
|
293
295
|
const storePath = getWorkspaceStorePath(root);
|
|
296
|
+
await waitForTodoStoreMirror();
|
|
297
|
+
await waitForPendingStatusEventMirrors();
|
|
294
298
|
if (!storeExistsSync(root)) {
|
|
295
299
|
return withFileLock(HANDOFF_REGISTRY_REL, () => registerHandoff(input));
|
|
296
300
|
}
|
|
@@ -332,9 +336,8 @@ export async function registerHandoffSafe(input) {
|
|
|
332
336
|
},
|
|
333
337
|
});
|
|
334
338
|
await store.commit();
|
|
335
|
-
const
|
|
336
|
-
await
|
|
337
|
-
await store.commit();
|
|
339
|
+
const projections = new ProjectionManager(store, root);
|
|
340
|
+
await projections.projectAfterCommit(["handoffs"]);
|
|
338
341
|
return {
|
|
339
342
|
record: toLegacyHandoffRecord(record),
|
|
340
343
|
path: wsPath(HANDOFF_REGISTRY_REL),
|
|
@@ -352,6 +355,8 @@ export async function registerHandoffSafe(input) {
|
|
|
352
355
|
export async function acknowledgeHandoffSafe(input) {
|
|
353
356
|
const root = workspaceRoot();
|
|
354
357
|
const storePath = getWorkspaceStorePath(root);
|
|
358
|
+
await waitForTodoStoreMirror();
|
|
359
|
+
await waitForPendingStatusEventMirrors();
|
|
355
360
|
if (!storeExistsSync(root)) {
|
|
356
361
|
return withFileLock(HANDOFF_REGISTRY_REL, () => acknowledgeHandoff(input));
|
|
357
362
|
}
|
|
@@ -403,9 +408,8 @@ export async function acknowledgeHandoffSafe(input) {
|
|
|
403
408
|
},
|
|
404
409
|
});
|
|
405
410
|
await store.commit();
|
|
406
|
-
const
|
|
407
|
-
await
|
|
408
|
-
await store.commit();
|
|
411
|
+
const projections = new ProjectionManager(store, root);
|
|
412
|
+
await projections.projectAfterCommit(["handoffs"]);
|
|
409
413
|
return {
|
|
410
414
|
ok: true,
|
|
411
415
|
path,
|