@voybio/ace-swarm 2.4.8 → 2.4.10
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/CHANGELOG.md +52 -14
- package/README.md +14 -10
- package/assets/.agents/ACE/ACE_coders/AGENTS.md +23 -30
- package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +21 -30
- package/assets/.agents/skills/landing-review-watcher/SKILL.md +1 -1
- package/assets/.agents/skills/learn-from-work/SKILL.md +41 -0
- package/assets/agent-state/ACE_WORKFLOW.md +25 -2
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +143 -2
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +2 -2
- package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +10 -0
- package/assets/eval-battery/battery.json +1 -1
- package/assets/instructions/ACE_Coder.instructions.md +9 -13
- package/assets/scripts/ace-hook-dispatch.mjs +16 -411
- package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +1 -2
- package/assets/vendor/README.md +9 -0
- package/assets/vendor/vericify-reader.mjs +272 -0
- package/assets/vendor/vericify-reader.provenance.json +10 -0
- package/dist/ace-autonomy.d.ts +58 -0
- package/dist/ace-autonomy.js +211 -6
- package/dist/ace-internal-tools.d.ts +1 -0
- package/dist/ace-internal-tools.js +3 -3
- package/dist/ace-server-instructions.d.ts +3 -3
- package/dist/ace-server-instructions.js +46 -192
- package/dist/cli.js +44 -6
- package/dist/experience-reflector.d.ts +7 -0
- package/dist/experience-reflector.js +39 -0
- package/dist/goal-engine.d.ts +64 -7
- package/dist/goal-engine.js +197 -41
- package/dist/handoff-registry.d.ts +10 -0
- package/dist/handoff-registry.js +59 -0
- package/dist/helpers/artifacts.d.ts +6 -2
- package/dist/helpers/artifacts.js +8 -6
- package/dist/helpers/bootstrap.js +13 -20
- package/dist/helpers/path-utils.js +2 -0
- package/dist/helpers/store-resolution.js +18 -3
- package/dist/helpers/workspace-root.d.ts +2 -0
- package/dist/helpers/workspace-root.js +9 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +1 -0
- package/dist/hermes/bridge-protocol.d.ts +1 -1
- package/dist/hermes/session-manager.d.ts +17 -0
- package/dist/hermes/session-manager.js +67 -14
- package/dist/host-goal-engine.d.ts +138 -0
- package/dist/host-goal-engine.js +793 -0
- package/dist/host-goal-status.d.ts +113 -0
- package/dist/host-goal-status.js +175 -0
- package/dist/host-learning.d.ts +68 -0
- package/dist/host-learning.js +115 -0
- package/dist/host-observation.d.ts +12 -0
- package/dist/host-observation.js +91 -0
- package/dist/host-plan-revision.d.ts +14 -0
- package/dist/host-plan-revision.js +101 -0
- package/dist/host-verification-job.d.ts +12 -0
- package/dist/host-verification-job.js +183 -0
- package/dist/host-work-context.d.ts +12 -0
- package/dist/host-work-context.js +8 -0
- package/dist/job-scheduler.d.ts +58 -13
- package/dist/job-scheduler.js +298 -48
- package/dist/job-worker.d.ts +3 -1
- package/dist/job-worker.js +10 -3
- package/dist/kanban.d.ts +4 -1
- package/dist/kanban.js +44 -16
- package/dist/learned-artifacts.d.ts +64 -0
- package/dist/learned-artifacts.js +321 -0
- package/dist/learning-controls.d.ts +142 -0
- package/dist/learning-controls.js +135 -0
- package/dist/learning-input-context.d.ts +9 -0
- package/dist/learning-input-context.js +62 -0
- package/dist/learning-lifecycle.d.ts +10 -0
- package/dist/learning-lifecycle.js +11 -0
- package/dist/learning-overlays.d.ts +3 -0
- package/dist/learning-overlays.js +17 -0
- package/dist/learning-policy.d.ts +40 -0
- package/dist/learning-policy.js +38 -0
- package/dist/learning-records.d.ts +35 -0
- package/dist/learning-records.js +17 -0
- package/dist/learning-scorecard.d.ts +82 -0
- package/dist/learning-scorecard.js +86 -0
- package/dist/learning-serving.d.ts +29 -0
- package/dist/learning-serving.js +134 -0
- package/dist/linked-work.d.ts +9 -0
- package/dist/linked-work.js +28 -0
- package/dist/local-model-runtime.js +1 -1
- package/dist/mcp-command-progress.d.ts +16 -0
- package/dist/mcp-command-progress.js +70 -0
- package/dist/model-bridge.d.ts +14 -13
- package/dist/model-bridge.js +73 -41
- package/dist/orchestrator-supervisor.d.ts +111 -6
- package/dist/orchestrator-supervisor.js +67 -43
- package/dist/plan-proposal.js +3 -3
- package/dist/playbook-curator.d.ts +1 -0
- package/dist/playbook-curator.js +11 -9
- package/dist/process-identity.d.ts +8 -0
- package/dist/process-identity.js +38 -0
- package/dist/progress-command.d.ts +34 -0
- package/dist/progress-command.js +153 -0
- package/dist/prompts.js +15 -15
- package/dist/resources.js +2 -2
- package/dist/runtime-command.d.ts +8 -1
- package/dist/runtime-command.js +48 -7
- package/dist/runtime-executor.js +33 -11
- package/dist/runtime-profile-syntax.d.ts +6 -0
- package/dist/runtime-profile-syntax.js +121 -0
- package/dist/runtime-profile.d.ts +37 -5
- package/dist/runtime-profile.js +36 -129
- package/dist/runtime-tool-specs.js +1 -1
- package/dist/safe-edit.d.ts +7 -3
- package/dist/safe-edit.js +40 -47
- package/dist/schemas.js +4 -2
- package/dist/server.d.ts +2 -16
- package/dist/server.js +14 -20
- package/dist/shared.d.ts +2 -0
- package/dist/shared.js +2 -0
- package/dist/skill-auditor.d.ts +19 -0
- package/dist/skill-auditor.js +32 -2
- package/dist/skill-catalog.d.ts +4 -1
- package/dist/skill-catalog.js +6 -5
- package/dist/store/ace-packed-store.d.ts +5 -1
- package/dist/store/ace-packed-store.js +33 -1
- package/dist/store/bootstrap-store.js +19 -12
- package/dist/store/cache-workspace.d.ts +8 -0
- package/dist/store/cache-workspace.js +94 -27
- package/dist/store/importer.js +11 -20
- package/dist/store/knowledge-bake.d.ts +3 -1
- package/dist/store/knowledge-bake.js +10 -1
- package/dist/store/legacy-vericify-posts.d.ts +8 -0
- package/dist/store/legacy-vericify-posts.js +76 -0
- package/dist/store/materializers/hook-context-materializer.d.ts +2 -1
- package/dist/store/materializers/hook-context-materializer.js +6 -2
- package/dist/store/materializers/host-file-materializer.js +2 -2
- package/dist/store/materializers/projection-manager.d.ts +4 -1
- package/dist/store/materializers/projection-manager.js +10 -3
- package/dist/store/materializers/todo-syncer.d.ts +39 -20
- package/dist/store/materializers/todo-syncer.js +208 -110
- package/dist/store/materializers/vericify-projector.js +15 -26
- package/dist/store/repositories/goal-repository.d.ts +51 -2
- package/dist/store/repositories/goal-repository.js +114 -48
- package/dist/store/repositories/playbook-repository.d.ts +41 -3
- package/dist/store/repositories/playbook-repository.js +61 -5
- package/dist/store/repositories/todo-repository.d.ts +8 -2
- package/dist/store/repositories/todo-repository.js +1 -1
- package/dist/store/repositories/vericify-repository.d.ts +4 -0
- package/dist/store/repositories/vericify-repository.js +46 -0
- package/dist/store/skills-install.d.ts +1 -0
- package/dist/store/skills-install.js +20 -16
- package/dist/store/source-preservation.d.ts +24 -0
- package/dist/store/source-preservation.js +79 -0
- package/dist/store/store-artifacts.js +8 -1
- package/dist/store/test-write-boundary.d.ts +16 -0
- package/dist/store/test-write-boundary.js +175 -0
- package/dist/store/types.d.ts +5 -1
- package/dist/store/write-coordinator.js +16 -0
- package/dist/task-plan-context.d.ts +11 -0
- package/dist/task-plan-context.js +59 -0
- package/dist/todo-state.d.ts +19 -9
- package/dist/todo-state.js +204 -321
- package/dist/tools-agent.d.ts +1 -1
- package/dist/tools-agent.js +38 -62
- package/dist/tools-files.d.ts +0 -3
- package/dist/tools-files.js +21 -11
- package/dist/tools-framework.js +16 -25
- package/dist/tools-goal.d.ts +2 -4
- package/dist/tools-goal.js +119 -16
- package/dist/tools-handoff.d.ts +3 -2
- package/dist/tools-handoff.js +53 -23
- package/dist/tools-lifecycle.js +1 -1
- package/dist/tools-memory.d.ts +0 -3
- package/dist/tools-memory.js +51 -6
- package/dist/tools-scheduler.js +20 -17
- package/dist/tools-todo.js +160 -148
- package/dist/vericify-bridge.d.ts +8 -0
- package/dist/vericify-bridge.js +18 -0
- package/dist/vericify-context.d.ts +4 -0
- package/dist/vericify-context.js +26 -5
- package/dist/vericify-native.d.ts +101 -0
- package/dist/vericify-native.js +221 -0
- package/dist/verification-worker.d.ts +2 -0
- package/dist/verification-worker.js +31 -0
- package/docs/mcp-tool-reference.md +50 -10
- package/docs/troubleshooting.md +29 -0
- package/package.json +19 -6
- package/scripts/dev/run-tests-isolated.mjs +165 -0
- package/scripts/dev/run_hermes_crispr_live.mjs +1 -0
- package/scripts/dev/test-isolation-guard.mjs +20 -0
- package/scripts/dev/test-preload.mjs +153 -0
- package/scripts/dev/test-source-inventory.mjs +31 -0
- package/docs/development.md +0 -46
|
@@ -1,140 +1,238 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* - On write: store → tasks/todo.md (ACE writes todo state from store)
|
|
7
|
-
* - On read: tasks/todo.md → store (if externally modified by mtime + hash)
|
|
8
|
-
*
|
|
9
|
-
* tasks/todo.md format:
|
|
10
|
-
* - [ ] Todo item title (id: todo-001)
|
|
11
|
-
* - [x] Completed item (id: todo-002)
|
|
12
|
-
* - [~] In progress (id: todo-003)
|
|
13
|
-
* - [!] Blocked (id: todo-004)
|
|
14
|
-
*/
|
|
15
|
-
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "fs";
|
|
16
|
-
import { join, dirname } from "path";
|
|
17
|
-
import { createHash } from "crypto";
|
|
1
|
+
/** Human-editable todo projection and its single inverse parser. */
|
|
2
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { EntityKind } from "../types.js";
|
|
18
6
|
import { TodoRepository } from "../repositories/todo-repository.js";
|
|
19
|
-
const STATUS_ICONS = {
|
|
20
|
-
pending: " ",
|
|
21
|
-
in_progress: "~",
|
|
22
|
-
done: "x",
|
|
23
|
-
blocked: "!",
|
|
24
|
-
cancelled: "-",
|
|
7
|
+
export const STATUS_ICONS = {
|
|
8
|
+
pending: " ", in_progress: "~", done: "x", blocked: "!", gated: "?", cancelled: "-",
|
|
25
9
|
};
|
|
26
|
-
const ICON_STATUS = {
|
|
27
|
-
" ": "pending",
|
|
28
|
-
"~": "in_progress",
|
|
29
|
-
x: "done",
|
|
30
|
-
"!": "blocked",
|
|
31
|
-
"-": "cancelled",
|
|
10
|
+
export const ICON_STATUS = {
|
|
11
|
+
" ": "pending", "~": "in_progress", x: "done", X: "done", "!": "blocked", "?": "gated", "-": "cancelled",
|
|
32
12
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
function
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
13
|
+
const CHECKSUMS_KEY = "host/materializations/checksums";
|
|
14
|
+
const TODO_REL = ".agents/ACE/tasks/todo.md";
|
|
15
|
+
const sha256 = (text) => createHash("sha256").update(text, "utf8").digest("hex");
|
|
16
|
+
export function parseTodoMd(content) {
|
|
17
|
+
const items = [];
|
|
18
|
+
const counts = new Map();
|
|
19
|
+
const import_warnings = [];
|
|
20
|
+
let section;
|
|
21
|
+
let notesTarget;
|
|
22
|
+
for (const [offset, line] of content.split(/\r?\n/).entries()) {
|
|
23
|
+
if (/^\s*>/.test(line)) {
|
|
24
|
+
if (notesTarget)
|
|
25
|
+
notesTarget.notes = [notesTarget.notes, line.replace(/^\s*>\s?/, "")].filter((v) => v !== undefined).join("\n");
|
|
26
|
+
else
|
|
27
|
+
import_warnings.push(`Line ${offset + 1}: unattached note retained in markdown.`);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
notesTarget = undefined;
|
|
31
|
+
const header = line.match(/^##\s+(.+)$/);
|
|
32
|
+
if (header) {
|
|
33
|
+
section = header[1].trim();
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const match = line.match(/^\s*-\s+\[([^\]]*)\]\s*(.*)$/);
|
|
37
|
+
if (!match) {
|
|
38
|
+
if (line.trim() && !/^#/.test(line))
|
|
39
|
+
import_warnings.push(`Line ${offset + 1}: unparseable todo row; retained canonical records.`);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const status = ICON_STATUS[match[1]];
|
|
43
|
+
const meta = {};
|
|
44
|
+
const metadata = (value) => {
|
|
45
|
+
for (const part of value.split(";")) {
|
|
46
|
+
const field = part.match(/^\s*(id|depends_on|depends)\s*[:=]\s*(.*?)\s*$/);
|
|
47
|
+
if (field)
|
|
48
|
+
meta[field[1]] = field[2];
|
|
49
|
+
}
|
|
50
|
+
return "";
|
|
51
|
+
};
|
|
52
|
+
let title = match[2].replace(/<!--\s*ace:([\s\S]*?)-->/g, (_, value) => metadata(value))
|
|
53
|
+
.replace(/\s*\((id\s*[:=][^)]*)\)\s*$/, (_, value) => metadata(value)).trim();
|
|
54
|
+
if (meta.id)
|
|
55
|
+
counts.set(meta.id, (counts.get(meta.id) ?? 0) + 1);
|
|
56
|
+
const assignee = title.match(/\s+@(\S+)$/);
|
|
57
|
+
if (assignee)
|
|
58
|
+
title = title.slice(0, assignee.index).trimEnd();
|
|
59
|
+
const priority = title.match(/\s+\[(high|medium|low)\]$/);
|
|
60
|
+
if (priority)
|
|
61
|
+
title = title.slice(0, priority.index).trimEnd();
|
|
62
|
+
if (!status || !title || ("id" in meta && !meta.id)) {
|
|
63
|
+
import_warnings.push(`Line ${offset + 1}: invalid checkbox, title or id; canonical row unchanged.`);
|
|
46
64
|
continue;
|
|
47
|
-
const icon = STATUS_ICONS[status];
|
|
48
|
-
for (const t of group) {
|
|
49
|
-
const priority = t.priority ? ` [${t.priority}]` : "";
|
|
50
|
-
const assignee = t.assigned_to ? ` @${t.assigned_to}` : "";
|
|
51
|
-
lines.push(`- [${icon}] ${t.title}${priority}${assignee} (id: ${t.id})`);
|
|
52
|
-
if (t.notes)
|
|
53
|
-
lines.push(` > ${t.notes}`);
|
|
54
65
|
}
|
|
66
|
+
const dependencies = meta.depends_on ?? meta.depends;
|
|
67
|
+
const item = { raw_line: line, id: meta.id, title, status, section, source_line: offset + 1,
|
|
68
|
+
...(dependencies === undefined ? {} : { depends_on: dependencies.split(",").map((s) => s.trim()).filter(Boolean) }),
|
|
69
|
+
...(priority ? { priority: priority[1] } : {}),
|
|
70
|
+
...(assignee ? { assigned_to: assignee[1] } : {}), };
|
|
71
|
+
items.push(item);
|
|
72
|
+
notesTarget = item;
|
|
55
73
|
}
|
|
56
|
-
|
|
74
|
+
for (const [id, count] of counts)
|
|
75
|
+
if (count > 1)
|
|
76
|
+
import_warnings.push(`Duplicate id ${id}; canonical row unchanged.`);
|
|
77
|
+
return { items: items.filter((item) => !item.id || counts.get(item.id) === 1), import_warnings };
|
|
57
78
|
}
|
|
58
|
-
function
|
|
59
|
-
const
|
|
60
|
-
for (const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const idMatch = rest.match(/\(id: ([^)]+)\)/);
|
|
68
|
-
const id = idMatch?.[1];
|
|
69
|
-
const title = rest.replace(/\s*\(id: [^)]+\)/, "").replace(/\s*\[[^\]]+\]/, "").replace(/\s*@\S+/, "").trim();
|
|
70
|
-
results.push({ id, title, status });
|
|
79
|
+
export function renderTodoMd(todos) {
|
|
80
|
+
const lines = ["# Tasks", ""];
|
|
81
|
+
for (const todo of todos) {
|
|
82
|
+
const priority = todo.priority ? ` [${todo.priority}]` : "";
|
|
83
|
+
const assignee = todo.assigned_to ? ` @${todo.assigned_to}` : "";
|
|
84
|
+
const deps = todo.depends_on?.length ? `; depends_on: ${todo.depends_on.join(",")}` : "";
|
|
85
|
+
lines.push(`- [${STATUS_ICONS[todo.status]}] ${todo.title}${priority}${assignee} (id: ${todo.id}${deps})`);
|
|
86
|
+
if (todo.notes)
|
|
87
|
+
lines.push(...todo.notes.split("\n").map((line) => ` > ${line}`));
|
|
71
88
|
}
|
|
72
|
-
return
|
|
89
|
+
return lines.join("\n") + "\n";
|
|
73
90
|
}
|
|
74
91
|
export class TodoSyncer {
|
|
75
92
|
store;
|
|
76
93
|
workspaceRoot;
|
|
77
94
|
repo;
|
|
78
|
-
|
|
95
|
+
submittedStamp;
|
|
79
96
|
todoMdPath;
|
|
80
97
|
constructor(store, workspaceRoot) {
|
|
81
98
|
this.store = store;
|
|
82
99
|
this.workspaceRoot = workspaceRoot;
|
|
83
100
|
this.repo = new TodoRepository(store);
|
|
84
|
-
|
|
85
|
-
this.todoMdPath = join(workspaceRoot, ".agents", "ACE", "tasks", "todo.md");
|
|
101
|
+
this.todoMdPath = join(workspaceRoot, TODO_REL);
|
|
86
102
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
async stamp() {
|
|
104
|
+
return (await this.store.getJSON(CHECKSUMS_KEY))?.[TODO_REL];
|
|
105
|
+
}
|
|
106
|
+
async setStamp(stamp) {
|
|
107
|
+
const all = await this.store.getJSON(CHECKSUMS_KEY) ?? {};
|
|
108
|
+
all[TODO_REL] = stamp;
|
|
109
|
+
await this.store.setJSON(CHECKSUMS_KEY, all);
|
|
110
|
+
}
|
|
111
|
+
/** Stage a non-destructive native import. The caller owns commit/discard and the coordinator. */
|
|
112
|
+
async syncFromFile(content, force = false, observeNative = true) {
|
|
113
|
+
if (content === undefined && !existsSync(this.todoMdPath))
|
|
114
|
+
return { changed: false, import_warnings: [] };
|
|
115
|
+
const raw = content ?? readFileSync(this.todoMdPath, "utf8");
|
|
116
|
+
const hash = sha256(raw);
|
|
117
|
+
const stamp = await this.stamp();
|
|
118
|
+
if (!force && hash === (stamp?.observed_hash ?? stamp?.hash)) {
|
|
119
|
+
if (!observeNative)
|
|
120
|
+
this.submittedStamp = stamp;
|
|
121
|
+
return { changed: false, import_warnings: stamp?.import_warnings ?? [] };
|
|
122
|
+
}
|
|
123
|
+
const { items, import_warnings } = parseTodoMd(raw);
|
|
124
|
+
const existing = await this.repo.getAll();
|
|
125
|
+
const byId = new Map(existing.map((todo) => [todo.id, todo]));
|
|
126
|
+
const seen = new Set();
|
|
127
|
+
const observed_rows = {};
|
|
128
|
+
// Reuse deletion history: a stale, unrendered file must not resurrect an explicit delete.
|
|
129
|
+
const deleted = new Set();
|
|
130
|
+
for (const entry of await this.store.getEntries({ kind: EntityKind.Todo })) {
|
|
131
|
+
const value = entry.payload;
|
|
132
|
+
if (!value?.id)
|
|
133
|
+
continue;
|
|
134
|
+
if (value.op === "remove")
|
|
135
|
+
deleted.add(value.id);
|
|
136
|
+
else
|
|
137
|
+
deleted.delete(value.id);
|
|
138
|
+
}
|
|
139
|
+
for (const item of items) {
|
|
140
|
+
let id = item.id;
|
|
141
|
+
if (!id) {
|
|
142
|
+
const matching = existing.filter((todo) => todo.title === item.title && !seen.has(todo.id));
|
|
143
|
+
if (matching.length === 1)
|
|
144
|
+
id = matching[0].id;
|
|
145
|
+
else {
|
|
146
|
+
const base = `todo-${item.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "item"}`;
|
|
147
|
+
id = base;
|
|
148
|
+
for (let n = 2; byId.has(id) || seen.has(id); n++)
|
|
149
|
+
id = `${base}-${n}`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
seen.add(id);
|
|
153
|
+
if (deleted.has(id) && !byId.has(id)) {
|
|
154
|
+
import_warnings.push(`Deleted todo ${id} remains in markdown; not recreated.`);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const prior = byId.get(id);
|
|
158
|
+
const rowHash = sha256(JSON.stringify([item.raw_line, item.notes ?? ""]));
|
|
159
|
+
observed_rows[id] = rowHash;
|
|
160
|
+
// Only changed native rows are edits. An unchanged stale row must not undo
|
|
161
|
+
// accepted work while another malformed row is being repaired.
|
|
162
|
+
if (prior && stamp?.observed_rows?.[id] === rowHash)
|
|
163
|
+
continue;
|
|
164
|
+
const { id: _id, raw_line: _raw, ...fields } = item;
|
|
165
|
+
if (!prior) {
|
|
166
|
+
const section = item.section?.toLowerCase() ?? "";
|
|
167
|
+
const status = item.status === "pending" && section.includes("verification") ? "gated" : item.status === "pending" && section.includes("active") ? "in_progress" : item.status;
|
|
168
|
+
const created = await this.repo.create({ ...fields, id, status });
|
|
169
|
+
byId.set(id, created);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
if (renderTodoMd([prior]).split("\n").some((line) => line === item.raw_line)) {
|
|
173
|
+
fields.title = prior.title;
|
|
174
|
+
fields.priority = prior.priority;
|
|
175
|
+
fields.assigned_to = prior.assigned_to;
|
|
176
|
+
fields.depends_on = prior.depends_on;
|
|
177
|
+
}
|
|
178
|
+
const patch = Object.fromEntries(Object.entries(fields).filter(([key, value]) => value !== undefined && JSON.stringify(prior[key]) !== JSON.stringify(value)));
|
|
179
|
+
if (prior.step_ref && patch.status !== undefined) {
|
|
180
|
+
delete patch.status;
|
|
181
|
+
import_warnings.push(`Todo ${id} is a step view; use complete_step for its status.`);
|
|
182
|
+
}
|
|
183
|
+
// Line numbers are view metadata; a reordered file alone need not rewrite the task.
|
|
184
|
+
delete patch.source_line;
|
|
185
|
+
if (Object.keys(patch).length)
|
|
186
|
+
await this.repo.update(id, patch);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
for (const todo of existing)
|
|
190
|
+
if (!seen.has(todo.id))
|
|
191
|
+
import_warnings.push(`Todo ${todo.id} missing from markdown; canonical record retained. Use explicit delete to remove it.`);
|
|
192
|
+
const nextStamp = { path: TODO_REL, hash: stamp?.hash ?? "", written_at: stamp?.written_at ?? 0, observed_hash: hash, observed_rows, import_warnings };
|
|
193
|
+
if (observeNative)
|
|
194
|
+
await this.setStamp(nextStamp);
|
|
195
|
+
else
|
|
196
|
+
this.submittedStamp = nextStamp;
|
|
197
|
+
return { changed: true, import_warnings };
|
|
96
198
|
}
|
|
97
|
-
/**
|
|
98
|
-
async
|
|
199
|
+
/** Supplied content is an observation only after its file publication succeeds. */
|
|
200
|
+
async acknowledgeSubmittedFile(content) {
|
|
201
|
+
if (this.submittedStamp?.observed_hash === sha256(content))
|
|
202
|
+
await this.setStamp(this.submittedStamp);
|
|
203
|
+
}
|
|
204
|
+
async projectionIsCurrent() {
|
|
99
205
|
if (!existsSync(this.todoMdPath))
|
|
100
206
|
return false;
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
continue;
|
|
113
|
-
if (existing.status !== item.status || existing.title !== item.title) {
|
|
114
|
-
await this.repo.update(item.id, { title: item.title, status: item.status });
|
|
115
|
-
changed = true;
|
|
207
|
+
const expected = sha256(renderTodoMd(await this.repo.getAll()));
|
|
208
|
+
return (await this.stamp())?.hash === expected && sha256(readFileSync(this.todoMdPath, "utf8")) === expected;
|
|
209
|
+
}
|
|
210
|
+
/** Do not overwrite unimported edits or malformed source text. */
|
|
211
|
+
async writeToFile() {
|
|
212
|
+
const stamp = await this.stamp();
|
|
213
|
+
const warnings = stamp?.import_warnings ?? [];
|
|
214
|
+
if (existsSync(this.todoMdPath)) {
|
|
215
|
+
const current = sha256(readFileSync(this.todoMdPath, "utf8"));
|
|
216
|
+
if (warnings.length || current !== (stamp?.observed_hash ?? stamp?.hash)) {
|
|
217
|
+
return { written: false, import_warnings: warnings.length ? warnings : ["Todo markdown changed before projection; native edit retained, refresh pending."] };
|
|
116
218
|
}
|
|
117
219
|
}
|
|
118
|
-
|
|
119
|
-
|
|
220
|
+
const content = renderTodoMd(await this.repo.getAll());
|
|
221
|
+
mkdirSync(dirname(this.todoMdPath), { recursive: true });
|
|
222
|
+
writeFileSync(this.todoMdPath, content, "utf8");
|
|
223
|
+
const hash = sha256(content);
|
|
224
|
+
const observed_rows = Object.fromEntries(parseTodoMd(content).items.filter((item) => item.id).map((item) => [item.id, sha256(JSON.stringify([item.raw_line, item.notes ?? ""]))]));
|
|
225
|
+
await this.setStamp({ path: TODO_REL, hash, observed_hash: hash, observed_rows, written_at: Date.now(), import_warnings: [] });
|
|
226
|
+
return { written: true, import_warnings: [] };
|
|
120
227
|
}
|
|
121
|
-
/** Initialize todo.md with starter content if it doesn't exist */
|
|
122
228
|
async initStarter() {
|
|
123
|
-
if (existsSync(this.todoMdPath))
|
|
229
|
+
if (existsSync(this.todoMdPath)) {
|
|
230
|
+
await this.syncFromFile();
|
|
124
231
|
return;
|
|
125
|
-
|
|
126
|
-
if (!
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
writeFileSync(this.todoMdPath, starter, "utf8");
|
|
130
|
-
this.lastHash = sha256(starter);
|
|
131
|
-
// Seed into store
|
|
132
|
-
await this.repo.create({
|
|
133
|
-
id: "todo-001",
|
|
134
|
-
title: "Initiate ACE",
|
|
135
|
-
status: "pending",
|
|
136
|
-
priority: "high",
|
|
137
|
-
});
|
|
232
|
+
}
|
|
233
|
+
if (!(await this.repo.getAll()).length)
|
|
234
|
+
await this.repo.create({ id: "todo-001", title: "Initiate ACE", status: "pending", priority: "high" });
|
|
235
|
+
await this.writeToFile();
|
|
138
236
|
}
|
|
139
237
|
}
|
|
140
238
|
//# sourceMappingURL=todo-syncer.js.map
|
|
@@ -19,7 +19,8 @@ import { HandoffRepository } from "../repositories/handoff-repository.js";
|
|
|
19
19
|
import { TodoRepository } from "../repositories/todo-repository.js";
|
|
20
20
|
import { LedgerRepository } from "../repositories/ledger-repository.js";
|
|
21
21
|
import { TrackerRepository } from "../repositories/tracker-repository.js";
|
|
22
|
-
import { VericifyRepository } from "../repositories/vericify-repository.js";
|
|
22
|
+
import { VericifyRepository, projectVericifyPost } from "../repositories/vericify-repository.js";
|
|
23
|
+
import { preserveLegacyVericifyPosts } from "../legacy-vericify-posts.js";
|
|
23
24
|
import { operationalArtifactKey } from "../store-artifacts.js";
|
|
24
25
|
// Runtime-facing state now lives in top-level agent-state/.
|
|
25
26
|
const ACE_STATE_ROOT = "agent-state";
|
|
@@ -74,6 +75,7 @@ export class VericifyProjector {
|
|
|
74
75
|
task_type: typeof metadata.task_type === "string" ? metadata.task_type : r.task,
|
|
75
76
|
priority: typeof metadata.priority === "string" ? metadata.priority : undefined,
|
|
76
77
|
source_file: typeof metadata.source_file === "string" ? metadata.source_file : undefined,
|
|
78
|
+
step_ref: metadata.step_ref,
|
|
77
79
|
status: legacyStatus,
|
|
78
80
|
history: Array.isArray(metadata.history) ? metadata.history : [],
|
|
79
81
|
};
|
|
@@ -90,10 +92,11 @@ export class VericifyProjector {
|
|
|
90
92
|
for (const t of todos) {
|
|
91
93
|
order.push(t.id);
|
|
92
94
|
nodes[t.id] = {
|
|
95
|
+
...t,
|
|
93
96
|
id: t.id,
|
|
94
97
|
title: t.title,
|
|
95
|
-
section: "",
|
|
96
|
-
source_line: 0,
|
|
98
|
+
section: t.section ?? (t.notes?.startsWith("section: ") ? t.notes.slice(9) : ""),
|
|
99
|
+
source_line: t.source_line ?? 0,
|
|
97
100
|
depends_on: t.depends_on ?? [],
|
|
98
101
|
status: mapStatus(t.status),
|
|
99
102
|
};
|
|
@@ -147,32 +150,16 @@ export class VericifyProjector {
|
|
|
147
150
|
await this._write("STATUS_EVENTS.ndjson", ndjson);
|
|
148
151
|
}
|
|
149
152
|
async projectVericifyPosts() {
|
|
153
|
+
const legacy = await preserveLegacyVericifyPosts(this.store, this.workspaceRoot);
|
|
154
|
+
for (const warning of legacy.warnings)
|
|
155
|
+
console.warn(warning);
|
|
156
|
+
if (!legacy.canProject)
|
|
157
|
+
return;
|
|
150
158
|
const posts = await this.vericify.list();
|
|
151
159
|
const content = JSON.stringify({
|
|
152
160
|
version: 1,
|
|
153
161
|
updated_at: new Date().toISOString(),
|
|
154
|
-
posts: posts.map(
|
|
155
|
-
process_post_id: post.id,
|
|
156
|
-
run_id: post.run_id,
|
|
157
|
-
agent_id: post.agent_id,
|
|
158
|
-
kind: post.kind,
|
|
159
|
-
summary: post.summary,
|
|
160
|
-
timestamp: new Date(post.ts).toISOString(),
|
|
161
|
-
branch_id: typeof post.metadata?.branch_id === "string" ? post.metadata.branch_id : undefined,
|
|
162
|
-
lane_id: typeof post.metadata?.lane_id === "string" ? post.metadata.lane_id : undefined,
|
|
163
|
-
blocker_category: typeof post.metadata?.blocker_category === "string"
|
|
164
|
-
? post.metadata.blocker_category
|
|
165
|
-
: undefined,
|
|
166
|
-
checkpoint_ref: typeof post.metadata?.checkpoint_ref === "string"
|
|
167
|
-
? post.metadata.checkpoint_ref
|
|
168
|
-
: undefined,
|
|
169
|
-
tool_refs: Array.isArray(post.metadata?.tool_refs)
|
|
170
|
-
? post.metadata.tool_refs.filter((value) => typeof value === "string")
|
|
171
|
-
: [],
|
|
172
|
-
evidence_refs: Array.isArray(post.metadata?.evidence_refs)
|
|
173
|
-
? post.metadata.evidence_refs.filter((value) => typeof value === "string")
|
|
174
|
-
: [],
|
|
175
|
-
})),
|
|
162
|
+
posts: posts.map(projectVericifyPost),
|
|
176
163
|
}, null, 2);
|
|
177
164
|
await this._write("vericify/process-posts.json", content);
|
|
178
165
|
}
|
|
@@ -203,8 +190,10 @@ function mapStatus(storeStatus) {
|
|
|
203
190
|
return "blocked";
|
|
204
191
|
case "done":
|
|
205
192
|
return "done";
|
|
193
|
+
case "gated":
|
|
194
|
+
return "gated";
|
|
206
195
|
case "cancelled":
|
|
207
|
-
return "
|
|
196
|
+
return "cancelled";
|
|
208
197
|
default:
|
|
209
198
|
return "backlog";
|
|
210
199
|
}
|
|
@@ -18,11 +18,21 @@ import type { TaskPlan } from "../../orchestrator-supervisor.js";
|
|
|
18
18
|
export type GoalStatus = "draft" | "planning" | "executing" | "verifying" | "blocked" | "done" | "failed" | "stopped";
|
|
19
19
|
export type GoalEscalationKind = "approval_required" | "run_complete" | "run_error" | "gate_failed" | "budget_blocked" | "blocker";
|
|
20
20
|
export interface GoalBudgets {
|
|
21
|
+
/** Zero means unlimited; only an explicitly marked allowance is enforced. */
|
|
21
22
|
max_wall_clock_ms: number;
|
|
23
|
+
/** Only an explicit host budget_hours request enables this duration. */
|
|
24
|
+
host_deadline_explicit?: boolean;
|
|
25
|
+
deadline_explicit?: boolean;
|
|
22
26
|
max_ticks: number;
|
|
27
|
+
tick_budget_explicit?: boolean;
|
|
23
28
|
max_replans: number;
|
|
29
|
+
replan_budget_explicit?: boolean;
|
|
24
30
|
max_step_retries: number;
|
|
25
31
|
}
|
|
32
|
+
/** Wire representation understood by pre-marker 2.4.x engines, which compare
|
|
33
|
+
* numeric limits directly. In-memory callers continue to use zero for unlimited.
|
|
34
|
+
* This is a finite compatibility sentinel, not a mathematical infinity. */
|
|
35
|
+
export declare const UNBOUNDED_GOAL_BUDGET_WIRE_VALUE: number;
|
|
26
36
|
export interface SuccessContract {
|
|
27
37
|
/** Union of step-level and goal-level required artifacts. */
|
|
28
38
|
expected_artifacts: Array<{
|
|
@@ -30,7 +40,7 @@ export interface SuccessContract {
|
|
|
30
40
|
required?: boolean;
|
|
31
41
|
}>;
|
|
32
42
|
required_evidence_refs: string[];
|
|
33
|
-
verification_commands:
|
|
43
|
+
verification_commands: import('../../goal-engine.js').VerificationCommand[];
|
|
34
44
|
/**
|
|
35
45
|
* TODO(WS-F): wire gate_ids to execute_gates. Always `[]` today and never
|
|
36
46
|
* read, so it is deliberately excluded from `enforceableObligationCount`.
|
|
@@ -61,12 +71,51 @@ export interface GoalRecord {
|
|
|
61
71
|
started_at?: string;
|
|
62
72
|
tick_count: number;
|
|
63
73
|
replan_count: number;
|
|
74
|
+
/** Consecutive unchanged failure sets, not a ceiling on productive corrections. */
|
|
75
|
+
failure_repetition?: {
|
|
76
|
+
signature: string;
|
|
77
|
+
count: number;
|
|
78
|
+
};
|
|
64
79
|
last_heartbeat_at?: string;
|
|
65
80
|
stop_requested: boolean;
|
|
66
81
|
escalations: GoalEscalation[];
|
|
67
82
|
result_summary?: string;
|
|
68
83
|
reason_code?: string;
|
|
69
84
|
current_step_id?: string;
|
|
85
|
+
executor?: "engine" | "host";
|
|
86
|
+
verified?: boolean;
|
|
87
|
+
verification_results?: Array<{
|
|
88
|
+
command: string;
|
|
89
|
+
ok: boolean;
|
|
90
|
+
output: string;
|
|
91
|
+
}>;
|
|
92
|
+
verification_attempt?: number;
|
|
93
|
+
verification_run?: {
|
|
94
|
+
job_id: string;
|
|
95
|
+
attempt: number;
|
|
96
|
+
enqueued_at: string;
|
|
97
|
+
status: 'queued' | 'running' | 'interrupted' | 'uncertain' | 'done' | 'failed' | 'stopped';
|
|
98
|
+
worker?: import('../../process-identity.js').ProcessIdentity;
|
|
99
|
+
command?: import('../../progress-command.js').CommandProgress & {
|
|
100
|
+
index: number;
|
|
101
|
+
command: string;
|
|
102
|
+
process?: import('../../process-identity.js').ProcessIdentity;
|
|
103
|
+
log_path?: string;
|
|
104
|
+
};
|
|
105
|
+
error?: string;
|
|
106
|
+
};
|
|
107
|
+
final_contract_check?: {
|
|
108
|
+
ok: boolean;
|
|
109
|
+
missing_artifacts: string[];
|
|
110
|
+
missing_evidence_refs: string[];
|
|
111
|
+
failed_commands: string[];
|
|
112
|
+
};
|
|
113
|
+
pending_follow_ups?: Array<{
|
|
114
|
+
step_id: string;
|
|
115
|
+
task: string;
|
|
116
|
+
role: string;
|
|
117
|
+
depends_on?: string[];
|
|
118
|
+
}>;
|
|
70
119
|
}
|
|
71
120
|
export interface GoalIndexEntry {
|
|
72
121
|
goal_id: string;
|
|
@@ -86,7 +135,7 @@ export declare class GoalRepository {
|
|
|
86
135
|
constructor(workspaceRoot: string);
|
|
87
136
|
private ensureDir;
|
|
88
137
|
private goalPath;
|
|
89
|
-
|
|
138
|
+
planPath(goalId: string): string;
|
|
90
139
|
private indexPath;
|
|
91
140
|
private writeAtomic;
|
|
92
141
|
private readJson;
|