@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.
Files changed (76) hide show
  1. package/README.md +69 -29
  2. package/assets/agent-state/EVIDENCE_LOG.md +1 -1
  3. package/assets/agent-state/STATUS.md +2 -2
  4. package/assets/scripts/ace-hook-dispatch.mjs +1 -1
  5. package/dist/ace-autonomy.js +38 -1
  6. package/dist/ace-context.js +8 -0
  7. package/dist/ace-server-instructions.js +55 -19
  8. package/dist/ace-state-resolver.d.ts +18 -0
  9. package/dist/ace-state-resolver.js +106 -0
  10. package/dist/cli.js +74 -7
  11. package/dist/handoff-registry.js +11 -7
  12. package/dist/helpers.js +75 -9
  13. package/dist/job-scheduler.js +94 -44
  14. package/dist/run-ledger.js +3 -4
  15. package/dist/server.d.ts +1 -1
  16. package/dist/server.js +1 -1
  17. package/dist/shared.d.ts +1 -1
  18. package/dist/status-events.js +12 -14
  19. package/dist/store/ace-packed-store.d.ts +65 -26
  20. package/dist/store/ace-packed-store.js +448 -261
  21. package/dist/store/bootstrap-store.d.ts +1 -1
  22. package/dist/store/bootstrap-store.js +24 -13
  23. package/dist/store/catalog-builder.js +3 -3
  24. package/dist/store/importer.d.ts +2 -2
  25. package/dist/store/importer.js +2 -2
  26. package/dist/store/materializers/context-snapshot-materializer.d.ts +10 -0
  27. package/dist/store/materializers/context-snapshot-materializer.js +51 -0
  28. package/dist/store/materializers/hook-context-materializer.d.ts +1 -1
  29. package/dist/store/materializers/hook-context-materializer.js +1 -1
  30. package/dist/store/materializers/host-file-materializer.d.ts +6 -0
  31. package/dist/store/materializers/host-file-materializer.js +14 -1
  32. package/dist/store/materializers/projection-manager.d.ts +14 -0
  33. package/dist/store/materializers/projection-manager.js +73 -0
  34. package/dist/store/materializers/scheduler-projection-materializer.d.ts +16 -0
  35. package/dist/store/materializers/scheduler-projection-materializer.js +48 -0
  36. package/dist/store/repositories/context-snapshot-repository.d.ts +46 -0
  37. package/dist/store/repositories/context-snapshot-repository.js +105 -0
  38. package/dist/store/repositories/local-model-runtime-repository.d.ts +98 -0
  39. package/dist/store/repositories/local-model-runtime-repository.js +165 -0
  40. package/dist/store/repositories/scheduler-repository.d.ts +21 -39
  41. package/dist/store/repositories/scheduler-repository.js +123 -93
  42. package/dist/store/repositories/todo-repository.d.ts +4 -0
  43. package/dist/store/repositories/todo-repository.js +50 -0
  44. package/dist/store/skills-install.d.ts +1 -1
  45. package/dist/store/skills-install.js +3 -3
  46. package/dist/store/state-reader.d.ts +8 -1
  47. package/dist/store/state-reader.js +19 -13
  48. package/dist/store/store-artifacts.js +105 -41
  49. package/dist/store/store-authority-audit.d.ts +30 -0
  50. package/dist/store/store-authority-audit.js +448 -0
  51. package/dist/store/store-snapshot.js +3 -3
  52. package/dist/store/types.d.ts +6 -2
  53. package/dist/store/types.js +5 -2
  54. package/dist/todo-state.js +179 -11
  55. package/dist/tools-files.js +2 -1
  56. package/dist/tools-framework.js +62 -2
  57. package/dist/tools-memory.js +69 -34
  58. package/dist/tools-todo.js +1 -1
  59. package/dist/tui/agent-worker.d.ts +1 -1
  60. package/dist/tui/agent-worker.js +5 -3
  61. package/dist/tui/chat.d.ts +19 -0
  62. package/dist/tui/chat.js +275 -9
  63. package/dist/tui/commands.d.ts +2 -0
  64. package/dist/tui/commands.js +62 -0
  65. package/dist/tui/dashboard.d.ts +6 -1
  66. package/dist/tui/dashboard.js +44 -3
  67. package/dist/tui/index.d.ts +5 -0
  68. package/dist/tui/index.js +154 -0
  69. package/dist/tui/input.js +5 -0
  70. package/dist/tui/layout.d.ts +24 -0
  71. package/dist/tui/layout.js +76 -2
  72. package/dist/tui/local-model-contract.d.ts +50 -0
  73. package/dist/tui/local-model-contract.js +272 -0
  74. package/dist/vericify-bridge.js +3 -4
  75. package/dist/vericify-context.js +18 -6
  76. 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` gives Claude Code, Codex, Cursor, VS Code, and local-model workflows a shared runtime: durable workspace state, typed handoffs, scheduler primitives, change intelligence, and operator tooling. It is built around a Zarrita-backed `.agents/ACE/ace-state.zarr` store, so the workspace keeps one source of truth instead of a forest of generated state files.
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 fills that gap by keeping the workflow in the store and projecting only the few files external clients still need. For local models, ACE provides the eyes and ears they usually lack: task context, tool surfaces, status events, evidence trails, and resumable state.
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.zarr`.
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 is the eyes and ears for local models. It gives them the context they cannot infer on their own: task state, tool surfaces, status events, evidence trails, and the next move in the loop.
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 gives that host a durable local runtime.
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
- ## Zarr State Model
36
+ ACE also provides scheduler primitives for queued work, dependency gates, leases, and resource locks so multi-agent runs stay coordinated.
37
37
 
38
- ACE uses `@zarrita/core` and `@zarrita/storage` to present `.agents/ACE/ace-state.zarr` as a hierarchical store.
39
- Zarrita.js supplies the Zarr v3 groups, arrays, and typed views; ACE supplies `AcePackedStore`, the single-file local backend that keeps the store portable and atomic.
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 `.zarr` authority | What it avoids |
86
+ | One `.ace` authority | What it avoids |
44
87
  |---|---|
45
- | One read/write source of truth | Hundreds of generated state files drifting apart |
46
- | Zarr groups and arrays | Hand-rolled file sprawl for runtime state, instructions, and indexes |
47
- | Typed hot fields | Repeated JSON parsing for counters and scheduler state |
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 indexes. Materialized files exist only when an external client still needs a path on disk.
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.zarr`; only a few host-facing files land in the workspace when requested.
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.zarr` — runtime state, agent instructions, skills, topology, schemas, and indexes
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.zarr
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 | 17 runtime roles: 4 swarm roles + 13 composable roles |
138
- | Skills | 15 packaged skills |
139
- | MCP server | stdio server for tools, prompts, resources, and workflows |
140
- | Durable state | single `.zarr` authority for handoffs, status events, todo state, run ledger, job queue, and discovery |
141
- | Projections | hook context, todo surface, and host configs when needed |
142
- | 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 |
143
186
  | Change intelligence | delta scan, semantic snapshots, drift reports, rewrite hints |
144
- | Terminal UI | provider-aware TUI with chat, tabs, telemetry, agent tabs |
145
- | Local models | Ollama and llama.cpp bootstrap, doctor checks, model bridge, tool loops |
146
- | Verification | 208 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 |
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.zarr`.
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
- - 2026-03-03T00:00:00Z | bootstrap initialized | evidence_ref: `EVIDENCE_LOG.md#ts:2026-03-03T00:00:00Z`
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: `capability-skeptic`
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: `2026-03-03T00:00:00Z`
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 .zarr store directly.
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");
@@ -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))
@@ -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
- `${ACE_ROOT_REL}/agent-state/TASK.md`,
6
- `${ACE_ROOT_REL}/agent-state/STATUS.md`,
7
- `${ACE_ROOT_REL}/agent-state/SCOPE.md`,
8
- `${ACE_ROOT_REL}/agent-state/EVIDENCE_LOG.md`,
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
- `${ACE_ROOT_REL}/agent-state`,
12
- `${ACE_ROOT_REL}/global-state`,
13
- `${ACE_ROOT_REL}/venture-state`,
14
- `${ACE_ROOT_REL}/brand-state`,
15
- `${ACE_ROOT_REL}/engineering-state`,
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) => existsSync(resolveWorkspacePath(workspaceRoot, 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 = readIfPresent(resolveWorkspacePath(workspaceRoot, "agent-state/ACE_WORKFLOW.md"));
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 = readIfPresent(resolveWorkspacePath(workspaceRoot, "agent-state/TASK.md"));
146
- const statusRaw = readIfPresent(resolveWorkspacePath(workspaceRoot, "agent-state/STATUS.md"));
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 inside `.agents/ACE/ace-state.zarr` and should be accessed through the ACE MCP server.",
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
- "- `.agents/ACE/ace-state.zarr` is authoritative.",
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 \`${ACE_ROOT_REL}/agent-state/EVIDENCE_LOG.md\`, run \`execute_gates\` when appropriate, and use structured handoffs/process posts for delegated follow-up.`,
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 = readIfPresent(resolveWorkspacePath(workspaceRoot, "agent-state/SCOPE.md"));
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 ${ACE_ROOT_REL}/agent-state/SCOPE.md before mutating files or state.`;
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