@voybio/ace-swarm 0.1.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 +69 -29
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/STATUS.md +2 -2
- package/assets/scripts/ace-hook-dispatch.mjs +1 -1
- 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 +74 -7
- package/dist/handoff-registry.js +11 -7
- package/dist/helpers.js +75 -9
- 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/ace-packed-store.d.ts +65 -26
- package/dist/store/ace-packed-store.js +448 -261
- package/dist/store/bootstrap-store.d.ts +1 -1
- package/dist/store/bootstrap-store.js +24 -13
- package/dist/store/catalog-builder.js +3 -3
- package/dist/store/importer.d.ts +2 -2
- package/dist/store/importer.js +2 -2
- 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/hook-context-materializer.d.ts +1 -1
- package/dist/store/materializers/hook-context-materializer.js +1 -1
- package/dist/store/materializers/host-file-materializer.d.ts +6 -0
- package/dist/store/materializers/host-file-materializer.js +14 -1
- 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/skills-install.d.ts +1 -1
- package/dist/store/skills-install.js +3 -3
- package/dist/store/state-reader.d.ts +8 -1
- package/dist/store/state-reader.js +19 -13
- package/dist/store/store-artifacts.js +105 -41
- package/dist/store/store-authority-audit.d.ts +30 -0
- package/dist/store/store-authority-audit.js +448 -0
- package/dist/store/store-snapshot.js +3 -3
- package/dist/store/types.d.ts +6 -2
- package/dist/store/types.js +5 -2
- package/dist/todo-state.js +179 -11
- package/dist/tools-files.js +2 -1
- package/dist/tools-framework.js +62 -2
- 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 +6 -1
- package/dist/tui/dashboard.js +44 -3
- package/dist/tui/index.d.ts +5 -0
- package/dist/tui/index.js +154 -0
- 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 +4 -6
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
|
|
|
@@ -12,11 +12,11 @@ Modern coding agents can already read files, write code, run commands, and call
|
|
|
12
12
|
- Handoffs should be typed, evidence-linked, and validated.
|
|
13
13
|
- Shared workflow state should live in the workspace, not only in chat history.
|
|
14
14
|
- Local models should get the same structure and observability as hosted models.
|
|
15
|
-
- Bootstrap should be contained and reversible: ACE writes into `.agents/ACE/ace-state.
|
|
15
|
+
- Bootstrap should be contained and reversible: ACE writes into `.agents/ACE/ace-state.ace`.
|
|
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,23 +31,66 @@ 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
35
|
|
|
36
|
-
|
|
36
|
+
ACE also provides scheduler primitives for queued work, dependency gates, leases, and resource locks so multi-agent runs stay coordinated.
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
## ACEPACK State Model
|
|
39
|
+
|
|
40
|
+
ACE uses a custom binary format called ACEPACK to present `.agents/ACE/ace-state.ace` as a structured single-file store. The file has three regions:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
ace-state.ace
|
|
44
|
+
├── Header (128 bytes)
|
|
45
|
+
│ magic · version · flags
|
|
46
|
+
│ kv_index_offset · kv_index_length · kv_chunk_end
|
|
47
|
+
│ evt_offset · evt_length · evt_count · evt_base_id
|
|
48
|
+
├── KV Chunk Region (append-only, random-access)
|
|
49
|
+
│ knowledge/agents/{name}/{file} → agent instruction text
|
|
50
|
+
│ knowledge/skills/{name}/{file} → skill content
|
|
51
|
+
│ topology/{kind} → swarm registry, route tables
|
|
52
|
+
│ state/{...} → handoffs, todo, ledger, scheduler
|
|
53
|
+
│ meta/{...} → schema version, project name
|
|
54
|
+
│ Each chunk: [uint32 length][bytes]
|
|
55
|
+
└── Columnar Event Section (rewritten on commit)
|
|
56
|
+
uint32 event count
|
|
57
|
+
int64[N] timestamps (epoch ms, big-endian)
|
|
58
|
+
uint8[N] kinds (EntityKind enum)
|
|
59
|
+
uint8[N] sources (ContentSource enum)
|
|
60
|
+
uint8[N] flags (0x01 = deleted)
|
|
61
|
+
uint32[N] payload offsets (→ payload pool)
|
|
62
|
+
uint32[N] payload lengths
|
|
63
|
+
uint32 pool length
|
|
64
|
+
bytes[M] payload pool (UTF-8 JSON blobs)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### KV region — random-access blob cache
|
|
68
|
+
|
|
69
|
+
All persistent state — agent instructions, skills, topology, task indexes, scheduler queues — lives in the KV region. Each key maps to an immutable chunk appended at write time. `commit()` rewrites the KV index (a small JSON map at the file tail) to reflect the live keys. `compact()` rewrites the KV region to remove dead space from overwritten keys, atomically via tmp-rename.
|
|
70
|
+
|
|
71
|
+
Write cost: O(1) append + O(index_size) index rewrite.
|
|
72
|
+
Read cost: O(1) seek + O(chunk_length) read.
|
|
73
|
+
|
|
74
|
+
### Columnar event section — typed handoff log
|
|
75
|
+
|
|
76
|
+
Every `appendEntry()` call — handoffs, status events, ledger entries, session events — lands in an in-memory buffer and is flushed to the columnar section on `commit()`. The columnar layout stores fixed-width fields (timestamp, kind, source, flags) separately from payloads. This means:
|
|
77
|
+
|
|
78
|
+
- Scanning "all handoffs in the last hour" reads only `timestamps[]` + `kinds[]` — 9 bytes per event, never touching the payload pool.
|
|
79
|
+
- Per-event overhead is ~19 bytes fixed vs ~150 bytes for equivalent JSON.
|
|
80
|
+
- At 100,000 events, the fixed columns are ~1.9 MB; a full JSON log would be ~15 MB.
|
|
81
|
+
|
|
82
|
+
Events accumulate for the workspace lifetime and survive `compact()`. Full historical replay is always available. See `HOT_COLD_EVENT_TIERING.md` for the future branch that adds compressed batch archiving for very long-lived workspaces.
|
|
40
83
|
|
|
41
84
|
### Why one state file
|
|
42
85
|
|
|
43
|
-
| One `.
|
|
86
|
+
| One `.ace` authority | What it avoids |
|
|
44
87
|
|---|---|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
88
|
+
| Single append-only file | Hundreds of generated state files drifting apart |
|
|
89
|
+
| KV region with typed keys | Hand-rolled file sprawl for runtime state, instructions, indexes |
|
|
90
|
+
| Columnar event log | Repeated full-JSON parsing for timestamp and kind scanning |
|
|
48
91
|
| Store-backed projections | External tools reading the same truth through different paths |
|
|
49
92
|
|
|
50
|
-
The store holds runtime state, agent instructions, skills, topology, schemas, and
|
|
93
|
+
The store holds runtime state, agent instructions, skills, topology, schemas, and the complete event history. Materialized files exist only when an external client still needs a path on disk.
|
|
51
94
|
|
|
52
95
|
## Quick Start
|
|
53
96
|
|
|
@@ -87,9 +130,9 @@ If you already have a local runtime running, `ace doctor --scan` will probe comm
|
|
|
87
130
|
|
|
88
131
|
## What Bootstrap Writes
|
|
89
132
|
|
|
90
|
-
ACE bootstrap is intentionally contained. The store is authoritative at `.agents/ACE/ace-state.
|
|
133
|
+
ACE bootstrap is intentionally contained. The store is authoritative at `.agents/ACE/ace-state.ace`; only a few host-facing files land in the workspace when requested.
|
|
91
134
|
|
|
92
|
-
- `.agents/ACE/ace-state.
|
|
135
|
+
- `.agents/ACE/ace-state.ace` — runtime state, agent instructions, skills, topology, schemas, and the event log
|
|
93
136
|
- `.agents/ACE/ace-hook-context.json` — compact hook snapshot for external clients
|
|
94
137
|
- `.agents/ACE/tasks/todo.md` — human-facing todo surface
|
|
95
138
|
- Optional workspace-root stubs — `AGENTS.md`, `CLAUDE.md`, `.cursorrules`, `.github/copilot-instructions.md`, and `.vscode/mcp.json`
|
|
@@ -115,7 +158,7 @@ Developer or MCP host
|
|
|
115
158
|
|
|
|
116
159
|
+-- .agents/ACE/
|
|
117
160
|
| |
|
|
118
|
-
| +-- ace-state.
|
|
161
|
+
| +-- ace-state.ace
|
|
119
162
|
| +-- ace-hook-context.json
|
|
120
163
|
| +-- tasks/todo.md
|
|
121
164
|
| `-- host config bundles
|
|
@@ -134,16 +177,16 @@ Developer or MCP host
|
|
|
134
177
|
+----------------------+--------------------------------------------------------------+
|
|
135
178
|
| Surface | Current scope |
|
|
136
179
|
+----------------------+--------------------------------------------------------------+
|
|
137
|
-
| Runtime roles |
|
|
138
|
-
| Skills |
|
|
139
|
-
| MCP server | stdio
|
|
140
|
-
| Durable state | single
|
|
141
|
-
| Projections | hook context, todo surface, and host
|
|
142
|
-
| Coordination |
|
|
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 |
|
|
143
186
|
| Change intelligence | delta scan, semantic snapshots, drift reports, rewrite hints |
|
|
144
|
-
| Terminal UI | provider-aware TUI with chat, tabs, telemetry, agent
|
|
145
|
-
| Local models | Ollama and llama.cpp bootstrap, doctor checks,
|
|
146
|
-
| Verification |
|
|
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 |
|
|
147
190
|
+----------------------+--------------------------------------------------------------+
|
|
148
191
|
```
|
|
149
192
|
|
|
@@ -162,7 +205,7 @@ In that setup, the host remains the interface. ACE becomes the state contract un
|
|
|
162
205
|
|
|
163
206
|
Local models are good at generating text. They usually need help seeing the workspace, hearing the handoff trail, and remembering what changed two turns ago. ACE closes that gap.
|
|
164
207
|
|
|
165
|
-
- `ace init --llm ollama` or `ace init --llm llama.cpp` seeds the profile inside `.agents/ACE/ace-state.
|
|
208
|
+
- `ace init --llm ollama` or `ace init --llm llama.cpp` seeds the profile inside `.agents/ACE/ace-state.ace`.
|
|
166
209
|
- `ace doctor` verifies that the selected runtime is reachable and configured.
|
|
167
210
|
- `ace tui` can talk to either local runtime and surface the same workspace state the MCP server sees.
|
|
168
211
|
- The ACE model bridge gives local runs tool selection, execution flow, and persisted state instead of a fragile one-shot chat loop.
|
|
@@ -199,7 +242,6 @@ If you want local agents with memory, evidence, and handoff discipline, this is
|
|
|
199
242
|
+---------------------------------------------+---------------------------------------------------+
|
|
200
243
|
```
|
|
201
244
|
|
|
202
|
-
|
|
203
245
|
## Development
|
|
204
246
|
|
|
205
247
|
```bash
|
|
@@ -216,10 +258,8 @@ See [CHANGELOG.md](./CHANGELOG.md) for release history.
|
|
|
216
258
|
|
|
217
259
|
- [Claude Code overview](https://docs.anthropic.com/en/docs/claude-code/overview)
|
|
218
260
|
- [OpenAI connectors and MCP servers](https://platform.openai.com/docs/guides/tools-remote-mcp?lang=python)
|
|
219
|
-
- [Introducing GPT-5.2-Codex](https://openai.com/index/introducing-gpt-5-2-codex/)
|
|
220
261
|
- [Ollama tool calling and agent loops](https://docs.ollama.com/capabilities/tool-calling)
|
|
221
262
|
- [llama.cpp](https://github.com/ggerganov/llama.cpp)
|
|
222
|
-
- [Zarrita.js](https://zarrita.dev/)
|
|
223
263
|
- [Model Context Protocol SDKs](https://modelcontextprotocol.io/docs/sdk)
|
|
224
264
|
|
|
225
265
|
## License
|
|
@@ -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}}`
|
|
@@ -206,7 +206,7 @@ function buildRoleToolHint(role) {
|
|
|
206
206
|
* Try to read the store-materialized hook context snapshot.
|
|
207
207
|
* Returns the parsed snapshot or undefined if not present / unreadable.
|
|
208
208
|
* When present this is the single authoritative source; the dispatcher
|
|
209
|
-
* never needs to open the .
|
|
209
|
+
* never needs to open the .ace store directly.
|
|
210
210
|
*/
|
|
211
211
|
function tryReadHookContextSnapshot(workspaceRoot) {
|
|
212
212
|
const snapshotPath = resolve(workspaceRoot, ACE_ROOT, "ace-hook-context.json");
|
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
|