@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
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Replaces the old copyTree()-based bootstrap with:
7
7
  * 1. Create minimal directories
8
- * 2. Initialize AcePackedStore at .agents/ACE/ace-state.zarr
8
+ * 2. Initialize AcePackedStore at .agents/ACE/ace-state.ace
9
9
  * 3. Bake core knowledge (agents, modules, kernel)
10
10
  * 4. Write topology records
11
11
  * 5. Write initial runtime state seeds
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Replaces the old copyTree()-based bootstrap with:
7
7
  * 1. Create minimal directories
8
- * 2. Initialize AcePackedStore at .agents/ACE/ace-state.zarr
8
+ * 2. Initialize AcePackedStore at .agents/ACE/ace-state.ace
9
9
  * 3. Bake core knowledge (agents, modules, kernel)
10
10
  * 4. Write topology records
11
11
  * 5. Write initial runtime state seeds
@@ -22,8 +22,8 @@ import { openStore } from "./ace-packed-store.js";
22
22
  import { bakeAllCoreKnowledge } from "./knowledge-bake.js";
23
23
  import { bakeTopology } from "./topology-bake.js";
24
24
  import { buildCatalog } from "./catalog-builder.js";
25
- import { HookContextMaterializer } from "./materializers/hook-context-materializer.js";
26
25
  import { HostFileMaterializer } from "./materializers/host-file-materializer.js";
26
+ import { ProjectionManager } from "./materializers/projection-manager.js";
27
27
  import { TodoSyncer } from "./materializers/todo-syncer.js";
28
28
  import { OPERATIONAL_ARTIFACT_REL_PATHS, operationalArtifactKey, } from "./store-artifacts.js";
29
29
  import { STORE_VERSION } from "./types.js";
@@ -99,21 +99,31 @@ async function materializeStoreSurfaces(store, workspaceRoot, opts) {
99
99
  includeClientConfigBundle: opts.includeClientConfigBundle ?? false,
100
100
  })));
101
101
  }
102
- const hookCtx = new HookContextMaterializer(store, workspaceRoot);
103
- await hookCtx.materialize();
104
- materialized.push(join(workspaceRoot, ".agents", "ACE", "ace-hook-context.json"));
105
102
  const todoSyncer = new TodoSyncer(store, workspaceRoot);
106
103
  await todoSyncer.initStarter();
107
- materialized.push(join(workspaceRoot, ".agents", "ACE", "tasks", "todo.md"));
108
104
  await store.commit();
105
+ const projections = new ProjectionManager(store, workspaceRoot);
106
+ await projections.projectAfterCommit(["hook_context", "todo_state", "scheduler", "context_snapshots"]);
107
+ materialized.push(join(workspaceRoot, ".agents", "ACE", "ace-hook-context.json"));
108
+ materialized.push(join(workspaceRoot, ".agents", "ACE", "tasks", "todo.md"));
109
+ materialized.push(join(workspaceRoot, ".agents", "ACE", "agent-state", "job-queue.json"));
110
+ materialized.push(join(workspaceRoot, ".agents", "ACE", "agent-state", "job-locks.json"));
111
+ materialized.push(join(workspaceRoot, ".agents", "ACE", "agent-state", "scheduler-lease.json"));
112
+ materialized.push(join(workspaceRoot, ".agents", "ACE", "agent-state", "context-snapshots", "index.json"));
109
113
  return [...new Set(materialized)];
110
114
  }
115
+ // Artifacts that receive live timestamp substitution on first seed.
116
+ const TIMESTAMPED_ARTIFACT_RELS = new Set(["agent-state/STATUS.md", "agent-state/EVIDENCE_LOG.md"]);
111
117
  async function seedOperationalArtifacts(store) {
118
+ const bootstrapTs = new Date().toISOString();
112
119
  for (const relPath of OPERATIONAL_ARTIFACT_REL_PATHS) {
113
120
  const knowledgeKey = `knowledge/agent-state/${relPath.replace(/^agent-state\//, "")}`;
114
- const content = relPath.endsWith("-archive.ndjson")
121
+ let content = relPath.endsWith("-archive.ndjson")
115
122
  ? ""
116
123
  : (await store.getBlob(knowledgeKey)) ?? "";
124
+ if (TIMESTAMPED_ARTIFACT_RELS.has(relPath) && content.includes("{{BOOTSTRAP_TIMESTAMP}}")) {
125
+ content = content.replaceAll("{{BOOTSTRAP_TIMESTAMP}}", bootstrapTs);
126
+ }
117
127
  await store.setBlob(operationalArtifactKey(relPath), content);
118
128
  }
119
129
  }
@@ -121,7 +131,7 @@ export async function bootstrapStoreWorkspace(opts) {
121
131
  const { workspaceRoot, force = false } = opts;
122
132
  const warnings = [];
123
133
  const materialized = [];
124
- const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.zarr");
134
+ const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.ace");
125
135
  if (existsSync(storePath) && !force) {
126
136
  if (!(opts.includeMcpConfig ?? false) && !(opts.includeClientConfigBundle ?? false) && !opts.llm) {
127
137
  warnings.push(`Store already exists at ${storePath}. Use --force to reinitialize, or run 'ace migrate' to import existing state.`);
@@ -168,6 +178,8 @@ export async function bootstrapStoreWorkspace(opts) {
168
178
  await store.setJSON("state/ledger/seq", 0);
169
179
  await store.setJSON("state/scheduler/queue", []);
170
180
  await store.setJSON("state/scheduler/locks", []);
181
+ await store.setJSON("state/scheduler/lease", null);
182
+ await store.setJSON("state/memory/context_snapshots/index.json", { snapshots: [] });
171
183
  await store.setJSON("state/runtime/sessions/index", []);
172
184
  await store.setJSON("state/discovery/index", []);
173
185
  await store.setJSON("state/vericify/posts/index", []);
@@ -194,7 +206,7 @@ export async function bootstrapStoreWorkspace(opts) {
194
206
  }
195
207
  // ── ace repair ────────────────────────────────────────────────────────────────
196
208
  export async function repairWorkspace(workspaceRoot) {
197
- const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.zarr");
209
+ const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.ace");
198
210
  if (!existsSync(storePath)) {
199
211
  return [`No store found at ${storePath}. Run 'ace init' first.`];
200
212
  }
@@ -208,10 +220,9 @@ export async function repairWorkspace(workspaceRoot) {
208
220
  includeMcpConfig: hostPolicy.include_mcp_config ?? false,
209
221
  includeClientConfigBundle: hostPolicy.include_client_config_bundle ?? false,
210
222
  });
211
- // Re-materialize hook context
212
- const hookCtx = new HookContextMaterializer(store, workspaceRoot);
213
- await hookCtx.materialize();
214
223
  await store.commit();
224
+ const projections = new ProjectionManager(store, workspaceRoot);
225
+ await projections.projectAfterCommit(["hook_context", "todo_state", "scheduler", "context_snapshots"]);
215
226
  }
216
227
  catch (e) {
217
228
  warnings.push(`repair error: ${e.message}`);
@@ -223,7 +234,7 @@ export async function repairWorkspace(workspaceRoot) {
223
234
  }
224
235
  // ── ace compact ───────────────────────────────────────────────────────────────
225
236
  export async function compactWorkspace(workspaceRoot) {
226
- const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.zarr");
237
+ const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.ace");
227
238
  if (!existsSync(storePath)) {
228
239
  throw new Error(`No store found at ${storePath}. Run 'ace init' first.`);
229
240
  }
@@ -44,10 +44,10 @@ export async function buildCatalog(store) {
44
44
  return catalog;
45
45
  }
46
46
  function collectKeysByPrefix(store, prefix) {
47
- // Access internal index synchronously — only call after store is opened
48
47
  const keys = [];
49
- // @ts-ignore — accessing private index for catalog building
50
- for (const key of store["index"].keys()) {
48
+ // @ts-ignore — accessing private kvIndex for synchronous catalog building
49
+ const kvIndex = store["kvIndex"];
50
+ for (const key of kvIndex.keys()) {
51
51
  if (key.startsWith(prefix))
52
52
  keys.push(key);
53
53
  }
@@ -2,10 +2,10 @@
2
2
  * Importer — Phase 7
3
3
  *
4
4
  * Reads all current files from an existing ACE workspace and creates a new
5
- * ace-state.zarr with all content properly categorized.
5
+ * ace-state.ace with all content properly categorized.
6
6
  *
7
7
  * Used by `ace migrate` CLI command for one-time migration from file-backed
8
- * workspaces to the Zarrita-backed store.
8
+ * workspaces to the ACEPACK store.
9
9
  */
10
10
  export interface ImportResult {
11
11
  handoffs: number;
@@ -2,10 +2,10 @@
2
2
  * Importer — Phase 7
3
3
  *
4
4
  * Reads all current files from an existing ACE workspace and creates a new
5
- * ace-state.zarr with all content properly categorized.
5
+ * ace-state.ace with all content properly categorized.
6
6
  *
7
7
  * Used by `ace migrate` CLI command for one-time migration from file-backed
8
- * workspaces to the Zarrita-backed store.
8
+ * workspaces to the ACEPACK store.
9
9
  */
10
10
  import { existsSync, readFileSync } from "fs";
11
11
  import { join } from "path";
@@ -0,0 +1,10 @@
1
+ import { AcePackedStore } from "../ace-packed-store.js";
2
+ export declare class ContextSnapshotMaterializer {
3
+ private store;
4
+ private workspaceRoot;
5
+ private repo;
6
+ constructor(store: AcePackedStore, workspaceRoot: string);
7
+ private directoryPath;
8
+ materializeAll(): Promise<void>;
9
+ }
10
+ //# sourceMappingURL=context-snapshot-materializer.d.ts.map
@@ -0,0 +1,51 @@
1
+ import { existsSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { ContextSnapshotRepository } from "../repositories/context-snapshot-repository.js";
4
+ function writeText(path, content) {
5
+ const dir = dirname(path);
6
+ if (!existsSync(dir))
7
+ mkdirSync(dir, { recursive: true });
8
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
9
+ writeFileSync(tmpPath, content, "utf-8");
10
+ renameSync(tmpPath, path);
11
+ }
12
+ export class ContextSnapshotMaterializer {
13
+ store;
14
+ workspaceRoot;
15
+ repo;
16
+ constructor(store, workspaceRoot) {
17
+ this.store = store;
18
+ this.workspaceRoot = workspaceRoot;
19
+ this.repo = new ContextSnapshotRepository(store);
20
+ }
21
+ directoryPath() {
22
+ return join(this.workspaceRoot, ".agents", "ACE", "agent-state", "context-snapshots");
23
+ }
24
+ async materializeAll() {
25
+ const dir = this.directoryPath();
26
+ if (!existsSync(dir))
27
+ mkdirSync(dir, { recursive: true });
28
+ const snapshots = await this.repo.listSnapshots();
29
+ const retained = new Set(["index.json"]);
30
+ for (const snapshot of snapshots) {
31
+ retained.add(snapshot.file);
32
+ writeText(join(dir, snapshot.file), `${JSON.stringify(snapshot.record, null, 2)}\n`);
33
+ }
34
+ writeText(join(dir, "index.json"), `${JSON.stringify({
35
+ snapshots: snapshots.map(({ name, file, timestamp, summary }) => ({
36
+ name,
37
+ file,
38
+ timestamp,
39
+ summary,
40
+ })),
41
+ }, null, 2)}\n`);
42
+ for (const file of readdirSync(dir)) {
43
+ if (!file.endsWith(".json"))
44
+ continue;
45
+ if (retained.has(file))
46
+ continue;
47
+ rmSync(join(dir, file), { force: true });
48
+ }
49
+ }
50
+ }
51
+ //# sourceMappingURL=context-snapshot-materializer.js.map
@@ -5,7 +5,7 @@
5
5
  * Called at every session start, prompt submit, and tool gating change.
6
6
  *
7
7
  * The hook dispatcher (ace-hook-dispatch.mjs) reads ONLY this file.
8
- * It never touches the .zarr store directly.
8
+ * It never touches the .ace store directly.
9
9
  */
10
10
  import { AcePackedStore } from "../ace-packed-store.js";
11
11
  export declare class HookContextMaterializer {
@@ -5,7 +5,7 @@
5
5
  * Called at every session start, prompt submit, and tool gating change.
6
6
  *
7
7
  * The hook dispatcher (ace-hook-dispatch.mjs) reads ONLY this file.
8
- * It never touches the .zarr store directly.
8
+ * It never touches the .ace store directly.
9
9
  */
10
10
  import { writeFileSync, mkdirSync, existsSync } from "fs";
11
11
  import { join, dirname } from "path";
@@ -32,6 +32,12 @@ export declare class HostFileMaterializer {
32
32
  private buildVsCodeMcpEntry;
33
33
  private buildAllProjectedEntries;
34
34
  seedStorePayload(): Promise<void>;
35
+ /**
36
+ * Materialize the full MCP client config bundle to `.mcp-config/` at the workspace root.
37
+ * Writes one config file per supported client plus hook files and a README.
38
+ * Called by `ace preconfig`.
39
+ */
40
+ materializeMcpBundle(): Promise<string[]>;
35
41
  materializeAll(opts?: HostMaterializeOptions): Promise<string[]>;
36
42
  }
37
43
  //# sourceMappingURL=host-file-materializer.d.ts.map
@@ -216,7 +216,7 @@ export class HostFileMaterializer {
216
216
  fallbackContent: [
217
217
  "# ACE MCP Client Config Bundle",
218
218
  "",
219
- `Generated for ${this.packageName}. Prefer global or user-level MCP installs; this bundle is stored in ace-state.zarr for export and repair.`,
219
+ `Generated for ${this.packageName}. Prefer global or user-level MCP installs; this bundle is stored in ace-state.ace for export and repair.`,
220
220
  "",
221
221
  "## Files",
222
222
  ...ALL_MCP_CLIENTS.map((client) => `- ${client}: ${fileNames[client]}`),
@@ -249,6 +249,19 @@ export class HostFileMaterializer {
249
249
  await this.seedProjectedText(entry);
250
250
  }
251
251
  }
252
+ /**
253
+ * Materialize the full MCP client config bundle to `.mcp-config/` at the workspace root.
254
+ * Writes one config file per supported client plus hook files and a README.
255
+ * Called by `ace preconfig`.
256
+ */
257
+ async materializeMcpBundle() {
258
+ await this.seedStorePayload();
259
+ const paths = [];
260
+ for (const entry of [...this.buildMcpBundleEntries(), ...this.buildOptionalHookBundleEntries()]) {
261
+ paths.push(await this.materializeEntry(entry));
262
+ }
263
+ return paths;
264
+ }
252
265
  async materializeAll(opts = {}) {
253
266
  const paths = [];
254
267
  const includeMcpConfig = opts.includeMcpConfig ?? false;
@@ -0,0 +1,14 @@
1
+ import { AcePackedStore } from "../ace-packed-store.js";
2
+ export type RuntimeProjectionTarget = "context_snapshots" | "handoffs" | "ledger" | "scheduler" | "status_events" | "todo_state" | "vericify_posts" | "hook_context";
3
+ export declare class ProjectionManager {
4
+ private store;
5
+ private workspaceRoot;
6
+ private vericify;
7
+ private todoSyncer;
8
+ private hookContext;
9
+ private contextSnapshots;
10
+ private scheduler;
11
+ constructor(store: AcePackedStore, workspaceRoot: string);
12
+ projectAfterCommit(targets: readonly RuntimeProjectionTarget[]): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=projection-manager.d.ts.map
@@ -0,0 +1,73 @@
1
+ import { ContextSnapshotMaterializer } from "./context-snapshot-materializer.js";
2
+ import { HookContextMaterializer } from "./hook-context-materializer.js";
3
+ import { SchedulerProjectionMaterializer } from "./scheduler-projection-materializer.js";
4
+ import { TodoSyncer } from "./todo-syncer.js";
5
+ import { VericifyProjector } from "./vericify-projector.js";
6
+ export class ProjectionManager {
7
+ store;
8
+ workspaceRoot;
9
+ vericify;
10
+ todoSyncer;
11
+ hookContext;
12
+ contextSnapshots;
13
+ scheduler;
14
+ constructor(store, workspaceRoot) {
15
+ this.store = store;
16
+ this.workspaceRoot = workspaceRoot;
17
+ this.vericify = new VericifyProjector(store, workspaceRoot);
18
+ this.todoSyncer = new TodoSyncer(store, workspaceRoot);
19
+ this.hookContext = new HookContextMaterializer(store, workspaceRoot);
20
+ this.contextSnapshots = new ContextSnapshotMaterializer(store, workspaceRoot);
21
+ this.scheduler = new SchedulerProjectionMaterializer(store, workspaceRoot);
22
+ }
23
+ async projectAfterCommit(targets) {
24
+ const uniqueTargets = [...new Set(targets)];
25
+ let stagedStoreWrites = false;
26
+ let renderedScheduler;
27
+ for (const target of uniqueTargets) {
28
+ if (target === "handoffs") {
29
+ await this.vericify.projectHandoffs();
30
+ stagedStoreWrites = true;
31
+ }
32
+ else if (target === "ledger") {
33
+ await this.vericify.projectLedger();
34
+ stagedStoreWrites = true;
35
+ }
36
+ else if (target === "status_events") {
37
+ await this.vericify.projectStatusEvents();
38
+ stagedStoreWrites = true;
39
+ }
40
+ else if (target === "todo_state") {
41
+ await this.vericify.projectTodos();
42
+ stagedStoreWrites = true;
43
+ }
44
+ else if (target === "vericify_posts") {
45
+ await this.vericify.projectVericifyPosts();
46
+ stagedStoreWrites = true;
47
+ }
48
+ else if (target === "scheduler") {
49
+ renderedScheduler = await this.scheduler.render();
50
+ await this.scheduler.stageStoreProjections(renderedScheduler);
51
+ stagedStoreWrites = true;
52
+ }
53
+ }
54
+ if (stagedStoreWrites) {
55
+ await this.store.commit();
56
+ }
57
+ for (const target of uniqueTargets) {
58
+ if (target === "todo_state") {
59
+ await this.todoSyncer.writeToFile();
60
+ }
61
+ else if (target === "hook_context") {
62
+ await this.hookContext.materialize();
63
+ }
64
+ else if (target === "context_snapshots") {
65
+ await this.contextSnapshots.materializeAll();
66
+ }
67
+ else if (target === "scheduler") {
68
+ await this.scheduler.materializeFiles(renderedScheduler);
69
+ }
70
+ }
71
+ }
72
+ }
73
+ //# sourceMappingURL=projection-manager.js.map
@@ -0,0 +1,16 @@
1
+ import { AcePackedStore } from "../ace-packed-store.js";
2
+ export declare class SchedulerProjectionMaterializer {
3
+ private store;
4
+ private workspaceRoot;
5
+ private repo;
6
+ constructor(store: AcePackedStore, workspaceRoot: string);
7
+ private filePath;
8
+ render(): Promise<{
9
+ queue: string;
10
+ locks: string;
11
+ lease: string;
12
+ }>;
13
+ stageStoreProjections(rendered?: Awaited<ReturnType<SchedulerProjectionMaterializer["render"]>>): Promise<void>;
14
+ materializeFiles(rendered?: Awaited<ReturnType<SchedulerProjectionMaterializer["render"]>>): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=scheduler-projection-materializer.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { existsSync, mkdirSync, renameSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { operationalArtifactKey } from "../store-artifacts.js";
4
+ import { SchedulerRepository } from "../repositories/scheduler-repository.js";
5
+ function writeText(path, content) {
6
+ const dir = dirname(path);
7
+ if (!existsSync(dir))
8
+ mkdirSync(dir, { recursive: true });
9
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
10
+ writeFileSync(tmpPath, content, "utf-8");
11
+ renameSync(tmpPath, path);
12
+ }
13
+ export class SchedulerProjectionMaterializer {
14
+ store;
15
+ workspaceRoot;
16
+ repo;
17
+ constructor(store, workspaceRoot) {
18
+ this.store = store;
19
+ this.workspaceRoot = workspaceRoot;
20
+ this.repo = new SchedulerRepository(store);
21
+ }
22
+ filePath(file) {
23
+ return join(this.workspaceRoot, ".agents", "ACE", "agent-state", file);
24
+ }
25
+ async render() {
26
+ const queue = await this.repo.readQueue();
27
+ const locks = await this.repo.readLockTable();
28
+ const lease = await this.repo.readLease();
29
+ return {
30
+ queue: `${JSON.stringify(queue, null, 2)}\n`,
31
+ locks: `${JSON.stringify(locks, null, 2)}\n`,
32
+ lease: `${JSON.stringify(lease ?? null, null, 2)}\n`,
33
+ };
34
+ }
35
+ async stageStoreProjections(rendered) {
36
+ const content = rendered ?? (await this.render());
37
+ await this.store.setBlob(operationalArtifactKey("agent-state/job-queue.json"), content.queue);
38
+ await this.store.setBlob(operationalArtifactKey("agent-state/job-locks.json"), content.locks);
39
+ await this.store.setBlob(operationalArtifactKey("agent-state/scheduler-lease.json"), content.lease);
40
+ }
41
+ async materializeFiles(rendered) {
42
+ const content = rendered ?? (await this.render());
43
+ writeText(this.filePath("job-queue.json"), content.queue);
44
+ writeText(this.filePath("job-locks.json"), content.locks);
45
+ writeText(this.filePath("scheduler-lease.json"), content.lease);
46
+ }
47
+ }
48
+ //# sourceMappingURL=scheduler-projection-materializer.js.map
@@ -0,0 +1,46 @@
1
+ import { AcePackedStore } from "../ace-packed-store.js";
2
+ export interface ContextSnapshotRecord {
3
+ name: string;
4
+ timestamp: string;
5
+ summary: string;
6
+ decisions: string[];
7
+ open_questions: string[];
8
+ artifacts: string[];
9
+ metadata: Record<string, unknown>;
10
+ }
11
+ export interface ContextSnapshotIndexEntry {
12
+ name: string;
13
+ file: string;
14
+ timestamp: string;
15
+ summary: string;
16
+ }
17
+ export interface SaveContextSnapshotInput {
18
+ name: string;
19
+ summary: string;
20
+ decisions?: string[];
21
+ open_questions?: string[];
22
+ artifacts?: string[];
23
+ metadata?: Record<string, unknown>;
24
+ timestamp?: string;
25
+ }
26
+ export declare class ContextSnapshotRepository {
27
+ private store;
28
+ constructor(store: AcePackedStore);
29
+ private snapshotKey;
30
+ getIndex(): Promise<ContextSnapshotIndexEntry[]>;
31
+ getSnapshotByFile(file: string): Promise<ContextSnapshotRecord | undefined>;
32
+ getSnapshotByName(name: string): Promise<{
33
+ entry: ContextSnapshotIndexEntry;
34
+ record: ContextSnapshotRecord;
35
+ } | undefined>;
36
+ saveSnapshot(input: SaveContextSnapshotInput): Promise<{
37
+ entry: ContextSnapshotIndexEntry;
38
+ record: ContextSnapshotRecord;
39
+ file: string;
40
+ }>;
41
+ listSnapshots(): Promise<Array<ContextSnapshotIndexEntry & {
42
+ record: ContextSnapshotRecord;
43
+ }>>;
44
+ private allocateFilename;
45
+ }
46
+ //# sourceMappingURL=context-snapshot-repository.d.ts.map
@@ -0,0 +1,105 @@
1
+ import { ContentSource, EntityKind } from "../types.js";
2
+ const INDEX_KEY = "state/memory/context_snapshots/index.json";
3
+ const SNAPSHOT_PREFIX = "state/memory/context_snapshots/";
4
+ function uniqueStrings(values) {
5
+ return Array.from(new Set((values ?? []).filter((value) => typeof value === "string" && value.trim().length > 0)));
6
+ }
7
+ function slugify(name) {
8
+ const base = name
9
+ .trim()
10
+ .toLowerCase()
11
+ .replace(/[^a-z0-9]+/g, "-")
12
+ .replace(/^-+|-+$/g, "")
13
+ .slice(0, 48);
14
+ return base.length > 0 ? base : "snapshot";
15
+ }
16
+ export class ContextSnapshotRepository {
17
+ store;
18
+ constructor(store) {
19
+ this.store = store;
20
+ }
21
+ snapshotKey(file) {
22
+ return `${SNAPSHOT_PREFIX}${file}`;
23
+ }
24
+ async getIndex() {
25
+ const index = await this.store.getJSON(INDEX_KEY);
26
+ if (Array.isArray(index))
27
+ return index;
28
+ return Array.isArray(index?.snapshots) ? index.snapshots : [];
29
+ }
30
+ async getSnapshotByFile(file) {
31
+ return this.store.getJSON(this.snapshotKey(file));
32
+ }
33
+ async getSnapshotByName(name) {
34
+ const index = await this.getIndex();
35
+ const entry = index.find((candidate) => candidate.name.toLowerCase() === name.toLowerCase());
36
+ if (!entry)
37
+ return undefined;
38
+ const record = await this.getSnapshotByFile(entry.file);
39
+ if (!record)
40
+ return undefined;
41
+ return { entry, record };
42
+ }
43
+ async saveSnapshot(input) {
44
+ const index = await this.getIndex();
45
+ const timestamp = input.timestamp ?? new Date().toISOString();
46
+ const existing = index.find((candidate) => candidate.name.toLowerCase() === input.name.toLowerCase());
47
+ const file = existing?.file ?? this.allocateFilename(index, input.name);
48
+ const record = {
49
+ name: input.name,
50
+ timestamp,
51
+ summary: input.summary,
52
+ decisions: uniqueStrings(input.decisions),
53
+ open_questions: uniqueStrings(input.open_questions),
54
+ artifacts: uniqueStrings(input.artifacts),
55
+ metadata: input.metadata && typeof input.metadata === "object" && !Array.isArray(input.metadata)
56
+ ? input.metadata
57
+ : {},
58
+ };
59
+ await this.store.setJSON(this.snapshotKey(file), record);
60
+ await this.store.appendEntry({
61
+ kind: EntityKind.ContextSnapshot,
62
+ content_source: ContentSource.Runtime,
63
+ key: this.snapshotKey(file),
64
+ payload: {
65
+ name: record.name,
66
+ file,
67
+ timestamp: record.timestamp,
68
+ summary: record.summary,
69
+ },
70
+ });
71
+ const nextIndex = index.filter((candidate) => candidate.name.toLowerCase() !== input.name.toLowerCase());
72
+ const entry = {
73
+ name: record.name,
74
+ file,
75
+ timestamp: record.timestamp,
76
+ summary: record.summary,
77
+ };
78
+ nextIndex.push(entry);
79
+ nextIndex.sort((left, right) => right.timestamp.localeCompare(left.timestamp));
80
+ await this.store.setJSON(INDEX_KEY, { snapshots: nextIndex });
81
+ return { entry, record, file };
82
+ }
83
+ async listSnapshots() {
84
+ const index = await this.getIndex();
85
+ const rows = [];
86
+ for (const entry of index) {
87
+ const record = await this.getSnapshotByFile(entry.file);
88
+ if (!record)
89
+ continue;
90
+ rows.push({ ...entry, record });
91
+ }
92
+ return rows;
93
+ }
94
+ allocateFilename(index, name) {
95
+ const base = slugify(name);
96
+ const used = new Set(index.map((entry) => entry.file));
97
+ if (!used.has(`${base}.json`))
98
+ return `${base}.json`;
99
+ let suffix = 2;
100
+ while (used.has(`${base}-${suffix}.json`))
101
+ suffix += 1;
102
+ return `${base}-${suffix}.json`;
103
+ }
104
+ }
105
+ //# sourceMappingURL=context-snapshot-repository.js.map