@voybio/ace-swarm 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/STATUS.md +2 -2
- package/dist/ace-autonomy.js +38 -1
- package/dist/ace-context.js +8 -0
- package/dist/ace-server-instructions.js +55 -19
- package/dist/ace-state-resolver.d.ts +18 -0
- package/dist/ace-state-resolver.js +106 -0
- package/dist/cli.js +67 -0
- package/dist/handoff-registry.js +11 -7
- package/dist/helpers.js +74 -8
- package/dist/job-scheduler.js +94 -44
- package/dist/run-ledger.js +3 -4
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/shared.d.ts +1 -1
- package/dist/status-events.js +12 -14
- package/dist/store/bootstrap-store.js +20 -9
- package/dist/store/materializers/context-snapshot-materializer.d.ts +10 -0
- package/dist/store/materializers/context-snapshot-materializer.js +51 -0
- package/dist/store/materializers/host-file-materializer.d.ts +6 -0
- package/dist/store/materializers/host-file-materializer.js +13 -0
- package/dist/store/materializers/projection-manager.d.ts +14 -0
- package/dist/store/materializers/projection-manager.js +73 -0
- package/dist/store/materializers/scheduler-projection-materializer.d.ts +16 -0
- package/dist/store/materializers/scheduler-projection-materializer.js +48 -0
- package/dist/store/repositories/context-snapshot-repository.d.ts +46 -0
- package/dist/store/repositories/context-snapshot-repository.js +105 -0
- package/dist/store/repositories/local-model-runtime-repository.d.ts +98 -0
- package/dist/store/repositories/local-model-runtime-repository.js +165 -0
- package/dist/store/repositories/scheduler-repository.d.ts +21 -39
- package/dist/store/repositories/scheduler-repository.js +123 -93
- package/dist/store/repositories/todo-repository.d.ts +4 -0
- package/dist/store/repositories/todo-repository.js +50 -0
- package/dist/store/state-reader.d.ts +8 -1
- package/dist/store/state-reader.js +12 -1
- package/dist/store/store-artifacts.js +31 -5
- package/dist/store/store-authority-audit.d.ts +30 -0
- package/dist/store/store-authority-audit.js +448 -0
- package/dist/store/types.d.ts +2 -0
- package/dist/store/types.js +1 -0
- package/dist/todo-state.js +179 -11
- package/dist/tools-files.js +2 -1
- package/dist/tools-framework.js +60 -0
- package/dist/tools-memory.js +69 -34
- package/dist/tools-todo.js +1 -1
- package/dist/tui/agent-worker.d.ts +1 -1
- package/dist/tui/agent-worker.js +5 -3
- package/dist/tui/chat.d.ts +19 -0
- package/dist/tui/chat.js +275 -9
- package/dist/tui/commands.d.ts +2 -0
- package/dist/tui/commands.js +62 -0
- package/dist/tui/dashboard.d.ts +5 -0
- package/dist/tui/dashboard.js +38 -2
- package/dist/tui/index.d.ts +5 -0
- package/dist/tui/index.js +146 -2
- package/dist/tui/input.js +5 -0
- package/dist/tui/layout.d.ts +24 -0
- package/dist/tui/layout.js +76 -2
- package/dist/tui/local-model-contract.d.ts +50 -0
- package/dist/tui/local-model-contract.js +272 -0
- package/dist/vericify-bridge.js +3 -4
- package/dist/vericify-context.js +18 -6
- package/package.json +1 -1
package/dist/todo-state.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { ACE_TASKS_ROOT_REL, resolveWorkspaceRoot, safeRead, safeWrite, wsPath } from "./helpers.js";
|
|
3
3
|
import { openStore } from "./store/ace-packed-store.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
5
|
+
import { TodoRepository } from "./store/repositories/todo-repository.js";
|
|
6
|
+
import { getWorkspaceStorePath, readStoreJsonSync, storeExistsSync } from "./store/store-snapshot.js";
|
|
7
|
+
import { operationalArtifactVirtualPath } from "./store/store-artifacts.js";
|
|
6
8
|
import { withStoreWriteQueue } from "./store/write-queue.js";
|
|
7
9
|
import { isReadError, slugify } from "./shared.js";
|
|
8
10
|
const TODO_MARKDOWN_REL = `${ACE_TASKS_ROOT_REL}/todo.md`;
|
|
@@ -182,9 +184,12 @@ function parseTodoState(raw) {
|
|
|
182
184
|
}
|
|
183
185
|
function readPersistedState() {
|
|
184
186
|
const raw = safeRead(TODO_STATE_REL);
|
|
185
|
-
if (isReadError(raw))
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
if (!isReadError(raw)) {
|
|
188
|
+
const parsed = parseTodoState(raw);
|
|
189
|
+
if (parsed)
|
|
190
|
+
return parsed;
|
|
191
|
+
}
|
|
192
|
+
return readStoreBackedState() ?? defaultState();
|
|
188
193
|
}
|
|
189
194
|
function mapStoreStatus(status) {
|
|
190
195
|
switch (status) {
|
|
@@ -200,8 +205,61 @@ function mapStoreStatus(status) {
|
|
|
200
205
|
return "pending";
|
|
201
206
|
}
|
|
202
207
|
}
|
|
203
|
-
|
|
208
|
+
function mapLegacyStatus(status) {
|
|
209
|
+
switch (status) {
|
|
210
|
+
case "in_progress":
|
|
211
|
+
return "in_progress";
|
|
212
|
+
case "blocked":
|
|
213
|
+
return "blocked";
|
|
214
|
+
case "done":
|
|
215
|
+
case "cancelled":
|
|
216
|
+
return "done";
|
|
217
|
+
case "pending":
|
|
218
|
+
default:
|
|
219
|
+
return "backlog";
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function readStoreBackedState() {
|
|
204
223
|
const root = workspaceRoot();
|
|
224
|
+
if (!storeExistsSync(root))
|
|
225
|
+
return undefined;
|
|
226
|
+
const index = readStoreJsonSync(root, "state/todo/index") ?? [];
|
|
227
|
+
if (index.length === 0)
|
|
228
|
+
return undefined;
|
|
229
|
+
const nodes = {};
|
|
230
|
+
const order = [];
|
|
231
|
+
let updatedAt = Date.now();
|
|
232
|
+
for (const id of index) {
|
|
233
|
+
const record = readStoreJsonSync(root, `state/todo/${id}`);
|
|
234
|
+
if (!record?.id || !record.title || !record.status)
|
|
235
|
+
continue;
|
|
236
|
+
const noteSection = typeof record.notes === "string" && record.notes.startsWith("section: ")
|
|
237
|
+
? record.notes.slice("section: ".length)
|
|
238
|
+
: "";
|
|
239
|
+
nodes[id] = {
|
|
240
|
+
id: record.id,
|
|
241
|
+
title: record.title,
|
|
242
|
+
section: noteSection,
|
|
243
|
+
source_line: 0,
|
|
244
|
+
depends_on: Array.isArray(record.depends_on)
|
|
245
|
+
? record.depends_on.filter((value) => typeof value === "string")
|
|
246
|
+
: [],
|
|
247
|
+
status: mapLegacyStatus(record.status),
|
|
248
|
+
};
|
|
249
|
+
order.push(id);
|
|
250
|
+
if (typeof record.updated_at === "number" && Number.isFinite(record.updated_at)) {
|
|
251
|
+
updatedAt = Math.max(updatedAt, record.updated_at);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
version: 1,
|
|
256
|
+
updated_at: new Date(updatedAt).toISOString(),
|
|
257
|
+
source_todo_path: TODO_MARKDOWN_REL,
|
|
258
|
+
order,
|
|
259
|
+
nodes,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
async function mirrorTodoStateToStore(root, state) {
|
|
205
263
|
const storePath = getWorkspaceStorePath(root);
|
|
206
264
|
if (!existsSync(storePath))
|
|
207
265
|
return;
|
|
@@ -230,29 +288,59 @@ async function mirrorTodoStateToStore(state) {
|
|
|
230
288
|
}
|
|
231
289
|
await store.setJSON("state/todo/index", nextIds);
|
|
232
290
|
await store.commit();
|
|
233
|
-
const
|
|
234
|
-
await
|
|
291
|
+
const projections = new ProjectionManager(store, root);
|
|
292
|
+
await projections.projectAfterCommit(["todo_state"]);
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
await store.close();
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
async function writeStateStoreBacked(root, state) {
|
|
300
|
+
const storePath = getWorkspaceStorePath(root);
|
|
301
|
+
if (!existsSync(storePath)) {
|
|
302
|
+
return writeState(state);
|
|
303
|
+
}
|
|
304
|
+
return withStoreWriteQueue(storePath, async () => {
|
|
305
|
+
const store = await openStore(storePath);
|
|
306
|
+
try {
|
|
307
|
+
const repo = new TodoRepository(store);
|
|
308
|
+
await repo.replaceAll(state.order
|
|
309
|
+
.map((id) => state.nodes[id])
|
|
310
|
+
.filter((node) => Boolean(node))
|
|
311
|
+
.map((node) => ({
|
|
312
|
+
id: node.id,
|
|
313
|
+
title: node.title,
|
|
314
|
+
status: mapStoreStatus(node.status),
|
|
315
|
+
priority: "medium",
|
|
316
|
+
depends_on: node.depends_on,
|
|
317
|
+
notes: node.section ? `section: ${node.section}` : undefined,
|
|
318
|
+
})));
|
|
235
319
|
await store.commit();
|
|
320
|
+
const projections = new ProjectionManager(store, root);
|
|
321
|
+
await projections.projectAfterCommit(["todo_state"]);
|
|
322
|
+
return operationalArtifactVirtualPath(root, TODO_STATE_REL);
|
|
236
323
|
}
|
|
237
324
|
finally {
|
|
238
325
|
await store.close();
|
|
239
326
|
}
|
|
240
327
|
});
|
|
241
328
|
}
|
|
242
|
-
function scheduleTodoMirror(state) {
|
|
329
|
+
function scheduleTodoMirror(root, state) {
|
|
243
330
|
const snapshot = JSON.parse(JSON.stringify(state));
|
|
244
331
|
todoMirrorChain = todoMirrorChain
|
|
245
332
|
.catch(() => { })
|
|
246
|
-
.then(() => mirrorTodoStateToStore(snapshot))
|
|
333
|
+
.then(() => mirrorTodoStateToStore(root, snapshot))
|
|
247
334
|
.catch(() => { });
|
|
248
335
|
}
|
|
249
336
|
export async function waitForTodoStoreMirror() {
|
|
250
337
|
await todoMirrorChain.catch(() => { });
|
|
251
338
|
}
|
|
252
339
|
function writeState(state) {
|
|
340
|
+
const root = workspaceRoot();
|
|
253
341
|
state.updated_at = new Date().toISOString();
|
|
254
342
|
const path = safeWrite(TODO_STATE_REL, JSON.stringify(state, null, 2));
|
|
255
|
-
scheduleTodoMirror(state);
|
|
343
|
+
scheduleTodoMirror(root, state);
|
|
256
344
|
return path;
|
|
257
345
|
}
|
|
258
346
|
function syncStateWithTodo(todoRaw, previous) {
|
|
@@ -314,11 +402,26 @@ export function syncTodoState(todoContent) {
|
|
|
314
402
|
return { state, path };
|
|
315
403
|
}
|
|
316
404
|
export async function syncTodoStateSafe(todoContent) {
|
|
405
|
+
const root = workspaceRoot();
|
|
406
|
+
if (storeExistsSync(root)) {
|
|
407
|
+
const todoRaw = typeof todoContent === "string" ? todoContent : safeRead(TODO_MARKDOWN_REL);
|
|
408
|
+
if (isReadError(todoRaw)) {
|
|
409
|
+
const state = defaultState();
|
|
410
|
+
const path = await writeStateStoreBacked(root, state);
|
|
411
|
+
return { state, path };
|
|
412
|
+
}
|
|
413
|
+
const state = syncStateWithTodo(todoRaw, readPersistedState());
|
|
414
|
+
const path = await writeStateStoreBacked(root, state);
|
|
415
|
+
return { state, path };
|
|
416
|
+
}
|
|
317
417
|
const result = syncTodoState(todoContent);
|
|
318
418
|
await waitForTodoStoreMirror();
|
|
319
419
|
return result;
|
|
320
420
|
}
|
|
321
421
|
export function readTodoState() {
|
|
422
|
+
if (storeExistsSync(workspaceRoot())) {
|
|
423
|
+
return readPersistedState();
|
|
424
|
+
}
|
|
322
425
|
const todoRaw = safeRead(TODO_MARKDOWN_REL);
|
|
323
426
|
if (isReadError(todoRaw))
|
|
324
427
|
return readPersistedState();
|
|
@@ -392,6 +495,71 @@ export function transitionTodoNode(nodeId, targetStatus) {
|
|
|
392
495
|
};
|
|
393
496
|
}
|
|
394
497
|
export async function transitionTodoNodeSafe(nodeId, targetStatus) {
|
|
498
|
+
const root = workspaceRoot();
|
|
499
|
+
if (storeExistsSync(root)) {
|
|
500
|
+
const todoRaw = safeRead(TODO_MARKDOWN_REL);
|
|
501
|
+
if (isReadError(todoRaw)) {
|
|
502
|
+
return {
|
|
503
|
+
ok: false,
|
|
504
|
+
path: getTodoStatePath(),
|
|
505
|
+
todo_path: getTodoPath(),
|
|
506
|
+
error: `${TODO_MARKDOWN_REL} not found; cannot apply transition.`,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
const synced = syncStateWithTodo(todoRaw, readPersistedState());
|
|
510
|
+
const node = synced.nodes[nodeId];
|
|
511
|
+
if (!node) {
|
|
512
|
+
return {
|
|
513
|
+
ok: false,
|
|
514
|
+
path: getTodoStatePath(),
|
|
515
|
+
todo_path: getTodoPath(),
|
|
516
|
+
error: `Unknown node_id: ${nodeId}`,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
const from = node.status;
|
|
520
|
+
if (from !== targetStatus) {
|
|
521
|
+
const allowed = TRANSITIONS[from];
|
|
522
|
+
if (!allowed.includes(targetStatus)) {
|
|
523
|
+
return {
|
|
524
|
+
ok: false,
|
|
525
|
+
path: getTodoStatePath(),
|
|
526
|
+
todo_path: getTodoPath(),
|
|
527
|
+
node_id: nodeId,
|
|
528
|
+
from,
|
|
529
|
+
to: targetStatus,
|
|
530
|
+
allowed,
|
|
531
|
+
error: `Invalid transition ${from} -> ${targetStatus}`,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (targetStatus === "in_progress" ||
|
|
536
|
+
targetStatus === "gated" ||
|
|
537
|
+
targetStatus === "done") {
|
|
538
|
+
const unresolved = node.depends_on.filter((depId) => synced.nodes[depId]?.status !== "done");
|
|
539
|
+
if (unresolved.length > 0) {
|
|
540
|
+
return {
|
|
541
|
+
ok: false,
|
|
542
|
+
path: getTodoStatePath(),
|
|
543
|
+
todo_path: getTodoPath(),
|
|
544
|
+
node_id: nodeId,
|
|
545
|
+
from,
|
|
546
|
+
to: targetStatus,
|
|
547
|
+
unresolved_dependencies: unresolved,
|
|
548
|
+
error: `Dependencies not satisfied for ${nodeId}`,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
node.status = targetStatus;
|
|
553
|
+
const statePath = await writeStateStoreBacked(root, synced);
|
|
554
|
+
return {
|
|
555
|
+
ok: true,
|
|
556
|
+
path: statePath,
|
|
557
|
+
todo_path: getTodoPath(),
|
|
558
|
+
node_id: nodeId,
|
|
559
|
+
from,
|
|
560
|
+
to: targetStatus,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
395
563
|
const result = transitionTodoNode(nodeId, targetStatus);
|
|
396
564
|
await waitForTodoStoreMirror();
|
|
397
565
|
return result;
|
package/dist/tools-files.js
CHANGED
|
@@ -470,12 +470,13 @@ export function registerFileTools(server) {
|
|
|
470
470
|
validationNotes.push(`lint warnings: ${lint.warnings.join("; ")}`);
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
|
-
const abs = safeWrite(path, content);
|
|
474
473
|
let todoStateSuffix = "";
|
|
475
474
|
if (normalizedPath === `${ACE_TASKS_ROOT_REL}/todo.md`) {
|
|
476
475
|
const synced = await syncTodoStateSafe(content);
|
|
477
476
|
todoStateSuffix = `\nTODO state synced: ${synced.path}`;
|
|
478
477
|
}
|
|
478
|
+
// Keep the file as a materialized projection after the canonical store update.
|
|
479
|
+
const abs = safeWrite(path, content);
|
|
479
480
|
let kanbanSuffix = "";
|
|
480
481
|
const shouldRefreshKanban = shouldAutoRefreshKanbanForPath(normalizedPath);
|
|
481
482
|
if (shouldRefreshKanban) {
|
package/dist/tools-framework.js
CHANGED
|
@@ -17,6 +17,7 @@ import { isGitRepo, gitStatus } from "./git-ops.js";
|
|
|
17
17
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
18
18
|
import { resolve } from "node:path";
|
|
19
19
|
import { auditPublicSurface } from "./public-surface.js";
|
|
20
|
+
import { auditStoreAuthority, writeStoreAuthorityAuditReport, } from "./store/store-authority-audit.js";
|
|
20
21
|
import { PROVENANCE_CRITICAL_EVENT_TYPES, validateArtifactManifestPayload, validateProvenanceLogContent, validateTealConfigContent, } from "./schemas.js";
|
|
21
22
|
import { readAceTaskContractAssessment } from "./ace-autonomy.js";
|
|
22
23
|
function getArtifactManifestEntries(payload) {
|
|
@@ -1003,6 +1004,28 @@ export function registerFrameworkTools(server) {
|
|
|
1003
1004
|
});
|
|
1004
1005
|
}
|
|
1005
1006
|
}
|
|
1007
|
+
const storeAuthority = auditStoreAuthority();
|
|
1008
|
+
checks.push({
|
|
1009
|
+
name: "store-authority:bypasses",
|
|
1010
|
+
ok: storeAuthority.bypassing_sites.length === 0,
|
|
1011
|
+
detail: storeAuthority.bypassing_sites.length === 0
|
|
1012
|
+
? "no runtime writers bypass ACEPACK"
|
|
1013
|
+
: `${storeAuthority.bypassing_sites.length} runtime writers still bypass ACEPACK`,
|
|
1014
|
+
});
|
|
1015
|
+
checks.push({
|
|
1016
|
+
name: "store-authority:runtime-fallbacks",
|
|
1017
|
+
ok: storeAuthority.runtime_files_without_store_fallback_keys.length === 0,
|
|
1018
|
+
detail: storeAuthority.runtime_files_without_store_fallback_keys.length === 0
|
|
1019
|
+
? "all runtime writers have store fallback keys"
|
|
1020
|
+
: `${storeAuthority.runtime_files_without_store_fallback_keys.length} runtime writers lack store fallback keys`,
|
|
1021
|
+
});
|
|
1022
|
+
checks.push({
|
|
1023
|
+
name: "store-authority:regenerable-projections",
|
|
1024
|
+
ok: storeAuthority.non_regenerable_projections.length === 0,
|
|
1025
|
+
detail: storeAuthority.non_regenerable_projections.length === 0
|
|
1026
|
+
? "all projections are regenerable from store state"
|
|
1027
|
+
: `${storeAuthority.non_regenerable_projections.length} projections are not regenerable from store state`,
|
|
1028
|
+
});
|
|
1006
1029
|
// Check: TEAL config has exactly one active YAML block
|
|
1007
1030
|
const tealRaw = safeRead("agent-state/TEAL_CONFIG.md");
|
|
1008
1031
|
if (!tealRaw.startsWith("[FILE NOT FOUND]")) {
|
|
@@ -1259,6 +1282,43 @@ export function registerFrameworkTools(server) {
|
|
|
1259
1282
|
],
|
|
1260
1283
|
};
|
|
1261
1284
|
});
|
|
1285
|
+
server.tool("audit_store_authority", "Report ACEPACK writer bypasses, missing fallback keys, and non-regenerable projections", {
|
|
1286
|
+
write_artifact: z
|
|
1287
|
+
.boolean()
|
|
1288
|
+
.optional()
|
|
1289
|
+
.describe("Write agent-state/STORE_AUTHORITY_AUDIT.md (default: true)"),
|
|
1290
|
+
artifact_path: z
|
|
1291
|
+
.string()
|
|
1292
|
+
.optional()
|
|
1293
|
+
.describe("Optional workspace-relative path for the audit report"),
|
|
1294
|
+
}, async ({ write_artifact, artifact_path }) => {
|
|
1295
|
+
const result = auditStoreAuthority();
|
|
1296
|
+
const reportPath = artifact_path ?? result.report_path;
|
|
1297
|
+
if (write_artifact !== false) {
|
|
1298
|
+
writeStoreAuthorityAuditReport({
|
|
1299
|
+
...result,
|
|
1300
|
+
report_path: reportPath,
|
|
1301
|
+
}, reportPath);
|
|
1302
|
+
}
|
|
1303
|
+
return {
|
|
1304
|
+
content: [
|
|
1305
|
+
{
|
|
1306
|
+
type: "text",
|
|
1307
|
+
text: [
|
|
1308
|
+
`✅ Store authority audit completed (${result.total_safe_write_sites} safeWrite users)`,
|
|
1309
|
+
`Report: ${reportPath}`,
|
|
1310
|
+
`Bypassing ACEPACK: ${result.bypassing_sites.length}`,
|
|
1311
|
+
`Runtime files without fallback keys: ${result.runtime_files_without_store_fallback_keys.length}`,
|
|
1312
|
+
`Non-regenerable projections: ${result.non_regenerable_projections.length}`,
|
|
1313
|
+
"",
|
|
1314
|
+
`- canonical knowledge artifacts: ${result.classifications["canonical knowledge artifact"].length}`,
|
|
1315
|
+
`- canonical runtime state writers: ${result.classifications["canonical runtime state"].length}`,
|
|
1316
|
+
`- projection-only writers: ${result.classifications["projection only"].length}`,
|
|
1317
|
+
].join("\n"),
|
|
1318
|
+
},
|
|
1319
|
+
],
|
|
1320
|
+
};
|
|
1321
|
+
});
|
|
1262
1322
|
// ── Execute Gates (manifest-driven gate runner) ───────────────────
|
|
1263
1323
|
server.tool("execute_gates", "Run quality gates from agent-state/MODULES/gates/*.json manifests (single source of truth)", {
|
|
1264
1324
|
gate_ids: z
|
package/dist/tools-memory.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Memory tool registrations – context snapshots and recall.
|
|
3
3
|
*/
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
4
5
|
import { z } from "zod";
|
|
5
|
-
import { safeRead, safeWrite, wsPath } from "./helpers.js";
|
|
6
|
+
import { resolveWorkspaceRoot, safeRead, safeWrite, wsPath } from "./helpers.js";
|
|
6
7
|
import { isReadError } from "./shared.js";
|
|
7
8
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
8
|
-
import {
|
|
9
|
+
import { appendStatusEventSafe } from "./status-events.js";
|
|
9
10
|
import { readRuntimeProfile } from "./runtime-profile.js";
|
|
10
11
|
import { buildAceContinuityPacket, formatAceContinuityPacketMarkdown, normalizeContinuityPolicy, } from "./ace-autonomy.js";
|
|
12
|
+
import { openStore } from "./store/ace-packed-store.js";
|
|
13
|
+
import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
14
|
+
import { ContextSnapshotRepository } from "./store/repositories/context-snapshot-repository.js";
|
|
15
|
+
import { getWorkspaceStorePath, storeExistsSync, toVirtualStorePath, } from "./store/store-snapshot.js";
|
|
16
|
+
import { withStoreWriteQueue } from "./store/write-queue.js";
|
|
11
17
|
const CONTEXT_STORE_REL = "agent-state/context-snapshots";
|
|
12
18
|
export function registerMemoryTools(server) {
|
|
13
19
|
server.tool("context_snapshot", "Capture a named context snapshot for later retrieval (decisions, progress, state)", {
|
|
@@ -31,39 +37,68 @@ export function registerMemoryTools(server) {
|
|
|
31
37
|
.describe("Additional metadata"),
|
|
32
38
|
}, async ({ name, summary, decisions, open_questions, artifacts, metadata }) => {
|
|
33
39
|
const timestamp = new Date().toISOString();
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
40
|
+
const root = resolveWorkspaceRoot();
|
|
41
|
+
let path = wsPath(CONTEXT_STORE_REL, `${name}.json`);
|
|
42
|
+
if (storeExistsSync(root) && existsSync(getWorkspaceStorePath(root))) {
|
|
43
|
+
const storePath = getWorkspaceStorePath(root);
|
|
44
|
+
const saved = await withStoreWriteQueue(storePath, async () => {
|
|
45
|
+
const store = await openStore(storePath);
|
|
46
|
+
try {
|
|
47
|
+
const repo = new ContextSnapshotRepository(store);
|
|
48
|
+
const result = await repo.saveSnapshot({
|
|
49
|
+
name,
|
|
50
|
+
summary,
|
|
51
|
+
decisions,
|
|
52
|
+
open_questions,
|
|
53
|
+
artifacts,
|
|
54
|
+
metadata,
|
|
55
|
+
timestamp,
|
|
56
|
+
});
|
|
57
|
+
await store.commit();
|
|
58
|
+
const projections = new ProjectionManager(store, root);
|
|
59
|
+
await projections.projectAfterCommit(["context_snapshots"]);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
await store.close();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
path = toVirtualStorePath(storePath, `state/memory/context_snapshots/${saved.file}`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const snapshot = {
|
|
70
|
+
name,
|
|
71
|
+
timestamp,
|
|
72
|
+
summary,
|
|
73
|
+
decisions: decisions ?? [],
|
|
74
|
+
open_questions: open_questions ?? [],
|
|
75
|
+
artifacts: artifacts ?? [],
|
|
76
|
+
metadata: metadata ?? {},
|
|
77
|
+
};
|
|
78
|
+
const slug = name
|
|
79
|
+
.trim()
|
|
80
|
+
.toLowerCase()
|
|
81
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
82
|
+
.slice(0, 48);
|
|
83
|
+
const filename = `${slug}.json`;
|
|
84
|
+
path = wsPath(CONTEXT_STORE_REL, filename);
|
|
85
|
+
safeWrite(path, JSON.stringify(snapshot, null, 2));
|
|
86
|
+
const indexPath = wsPath(CONTEXT_STORE_REL, "index.json");
|
|
87
|
+
const indexRaw = safeRead(indexPath);
|
|
88
|
+
let index = { snapshots: [] };
|
|
89
|
+
if (!isReadError(indexRaw)) {
|
|
90
|
+
try {
|
|
91
|
+
index = JSON.parse(indexRaw);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
/* keep default */
|
|
95
|
+
}
|
|
61
96
|
}
|
|
97
|
+
index.snapshots = index.snapshots.filter((s) => s.name !== name);
|
|
98
|
+
index.snapshots.push({ name, file: filename, timestamp, summary });
|
|
99
|
+
index.snapshots.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
100
|
+
safeWrite(indexPath, JSON.stringify(index, null, 2));
|
|
62
101
|
}
|
|
63
|
-
index.snapshots = index.snapshots.filter((s) => s.name !== name);
|
|
64
|
-
index.snapshots.push({ name, file: filename, timestamp, summary });
|
|
65
|
-
index.snapshots.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
66
|
-
safeWrite(indexPath, JSON.stringify(index, null, 2));
|
|
67
102
|
const ledger = await appendRunLedgerEntrySafe({
|
|
68
103
|
tool: "context_snapshot",
|
|
69
104
|
category: "info",
|
|
@@ -74,7 +109,7 @@ export function registerMemoryTools(server) {
|
|
|
74
109
|
decisions_count: (decisions ?? []).length,
|
|
75
110
|
},
|
|
76
111
|
});
|
|
77
|
-
|
|
112
|
+
await appendStatusEventSafe({
|
|
78
113
|
source_module: "capability-memory",
|
|
79
114
|
event_type: "CONTEXT_SNAPSHOT",
|
|
80
115
|
status: "done",
|
package/dist/tools-todo.js
CHANGED
|
@@ -32,8 +32,8 @@ export function registerTodoTools(server) {
|
|
|
32
32
|
note: z.string().optional().describe("Optional transition context note"),
|
|
33
33
|
}, async ({ new_content, node_id, status, note }) => {
|
|
34
34
|
if (typeof new_content === "string") {
|
|
35
|
-
const path = safeWrite(resolveWritableTaskPath("todo"), new_content);
|
|
36
35
|
const synced = await syncTodoStateSafe(new_content);
|
|
36
|
+
const path = safeWrite(resolveWritableTaskPath("todo"), new_content);
|
|
37
37
|
const runLedger = await appendRunLedgerEntrySafe({
|
|
38
38
|
tool: "update_todo",
|
|
39
39
|
category: "major_update",
|
package/dist/tui/agent-worker.js
CHANGED
|
@@ -7,11 +7,10 @@
|
|
|
7
7
|
* unconfigured runtimes.
|
|
8
8
|
*/
|
|
9
9
|
import { parseManualHandoffCommand, parseManualToolCommand, planRoleExecution, } from "../agent-runtime/role-adapters.js";
|
|
10
|
-
import { existsSync } from "node:fs";
|
|
11
|
-
import { resolve } from "node:path";
|
|
12
10
|
import { ModelBridge } from "../model-bridge.js";
|
|
13
11
|
import { OllamaClient } from "./ollama.js";
|
|
14
12
|
import { OpenAICompatibleClient, diagnoseChatRuntimeConfig, } from "./openai-compatible.js";
|
|
13
|
+
import { resolveAceStateLayout } from "../ace-state-resolver.js";
|
|
15
14
|
const config = new Map();
|
|
16
15
|
let role = process.env.ACE_TUI_AGENT_ROLE ?? "unknown";
|
|
17
16
|
let initialized = false;
|
|
@@ -22,6 +21,9 @@ const bridge = new ModelBridge({
|
|
|
22
21
|
ollama: ollamaClient,
|
|
23
22
|
openai: openaiClient,
|
|
24
23
|
});
|
|
24
|
+
export function canUseAceBridgeWorkspace(workspaceRoot) {
|
|
25
|
+
return resolveAceStateLayout(workspaceRoot).isAcePresent;
|
|
26
|
+
}
|
|
25
27
|
function send(message) {
|
|
26
28
|
if (typeof process.send === "function") {
|
|
27
29
|
process.send(message);
|
|
@@ -101,7 +103,7 @@ async function tryModelBridge(text) {
|
|
|
101
103
|
const workspace = config.get("workspace_root")?.trim();
|
|
102
104
|
if (!provider || !model || !workspace)
|
|
103
105
|
return false;
|
|
104
|
-
if (!
|
|
106
|
+
if (!canUseAceBridgeWorkspace(workspace)) {
|
|
105
107
|
return false;
|
|
106
108
|
}
|
|
107
109
|
if (provider.toLowerCase() === "ollama" && !baseUrl) {
|
package/dist/tui/chat.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { EventEmitter } from "node:events";
|
|
|
11
11
|
import type { ChatMessage } from "./layout.js";
|
|
12
12
|
import { ModelBridge } from "../model-bridge.js";
|
|
13
13
|
import type { AceContextTier } from "../ace-context.js";
|
|
14
|
+
import { type AceRuntimeStatusPacket } from "../store/repositories/local-model-runtime-repository.js";
|
|
14
15
|
export interface ChatSessionOptions {
|
|
15
16
|
provider: string;
|
|
16
17
|
model: string;
|
|
@@ -53,9 +54,21 @@ export declare class ChatSession extends EventEmitter {
|
|
|
53
54
|
private aceBridge;
|
|
54
55
|
private activeAceBridge;
|
|
55
56
|
private providerBaseUrls;
|
|
57
|
+
private sessionId;
|
|
58
|
+
private createdAt;
|
|
59
|
+
private lastActiveAt;
|
|
60
|
+
private currentRuntimeStatus;
|
|
61
|
+
private activationLedger;
|
|
56
62
|
constructor(clients: ChatSessionClients, telemetry: TelemetryTracker, options: ChatSessionOptions);
|
|
57
63
|
/** Get display messages for rendering */
|
|
58
64
|
getDisplayMessages(): ChatMessage[];
|
|
65
|
+
getSessionId(): string;
|
|
66
|
+
getCreatedAt(): number;
|
|
67
|
+
getLastActiveAt(): number;
|
|
68
|
+
getCurrentRuntimeStatus(): AceRuntimeStatusPacket | undefined;
|
|
69
|
+
hasMeaningfulTranscript(): boolean;
|
|
70
|
+
getTranscriptExcerpt(maxMessages?: number): string | undefined;
|
|
71
|
+
getTranscriptSummary(): string | undefined;
|
|
59
72
|
/** Is currently streaming a response? */
|
|
60
73
|
isStreaming(): boolean;
|
|
61
74
|
/** Get current model */
|
|
@@ -78,6 +91,12 @@ export declare class ChatSession extends EventEmitter {
|
|
|
78
91
|
private shouldUseAceBridge;
|
|
79
92
|
private buildBridgeTask;
|
|
80
93
|
private pushSystemMessage;
|
|
94
|
+
private noteActivity;
|
|
95
|
+
private buildRuntimeStatusPacket;
|
|
96
|
+
private setRuntimeStatus;
|
|
97
|
+
setApprovalState(state: NonNullable<AceRuntimeStatusPacket["approval_state"]>): boolean;
|
|
98
|
+
private persistRuntimeStatus;
|
|
99
|
+
private persistRuntimeSessionArtifacts;
|
|
81
100
|
private runAceBridge;
|
|
82
101
|
}
|
|
83
102
|
export {};
|