@voybio/ace-swarm 0.2.5 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -1
- package/README.md +21 -13
- package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
- package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
- package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +43 -0
- package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
- package/assets/agent-state/STATUS.md +2 -2
- package/assets/agent-state/runtime-tool-specs.json +70 -2
- package/assets/instructions/ACE_Coder.instructions.md +13 -0
- package/assets/instructions/ACE_UI.instructions.md +11 -0
- package/assets/scripts/ace-hook-dispatch.mjs +70 -6
- package/assets/scripts/render-mcp-configs.sh +19 -5
- package/dist/ace-context.js +91 -11
- package/dist/ace-internal-tools.d.ts +3 -1
- package/dist/ace-internal-tools.js +10 -2
- package/dist/ace-server-instructions.js +3 -3
- package/dist/ace-state-resolver.js +5 -3
- package/dist/agent-runtime/role-adapters.d.ts +18 -1
- package/dist/agent-runtime/role-adapters.js +49 -5
- package/dist/astgrep-index.d.ts +57 -1
- package/dist/astgrep-index.js +140 -4
- package/dist/cli.js +232 -35
- package/dist/discovery-runtime-wrappers.d.ts +108 -0
- package/dist/discovery-runtime-wrappers.js +615 -0
- package/dist/handoff-registry.js +5 -5
- package/dist/helpers/artifacts.d.ts +19 -0
- package/dist/helpers/artifacts.js +152 -0
- package/dist/helpers/bootstrap.d.ts +24 -0
- package/dist/helpers/bootstrap.js +894 -0
- package/dist/helpers/constants.d.ts +53 -0
- package/dist/helpers/constants.js +295 -0
- package/dist/helpers/drift.d.ts +13 -0
- package/dist/helpers/drift.js +45 -0
- package/dist/helpers/path-utils.d.ts +24 -0
- package/dist/helpers/path-utils.js +123 -0
- package/dist/helpers/store-resolution.d.ts +19 -0
- package/dist/helpers/store-resolution.js +305 -0
- package/dist/helpers/workspace-root.d.ts +3 -0
- package/dist/helpers/workspace-root.js +80 -0
- package/dist/helpers.d.ts +8 -125
- package/dist/helpers.js +8 -1768
- package/dist/job-scheduler.js +33 -7
- package/dist/json-sanitizer.d.ts +16 -0
- package/dist/json-sanitizer.js +26 -0
- package/dist/local-model-policy.d.ts +27 -0
- package/dist/local-model-policy.js +84 -0
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +33 -21
- package/dist/model-bridge.d.ts +13 -1
- package/dist/model-bridge.js +410 -23
- package/dist/orchestrator-supervisor.d.ts +56 -0
- package/dist/orchestrator-supervisor.js +179 -1
- package/dist/plan-proposal.d.ts +115 -0
- package/dist/plan-proposal.js +1073 -0
- package/dist/run-ledger.js +3 -3
- package/dist/runtime-command.d.ts +8 -0
- package/dist/runtime-command.js +38 -6
- package/dist/runtime-executor.d.ts +20 -1
- package/dist/runtime-executor.js +737 -172
- package/dist/runtime-profile.d.ts +32 -0
- package/dist/runtime-profile.js +89 -13
- package/dist/runtime-tool-specs.d.ts +39 -0
- package/dist/runtime-tool-specs.js +144 -28
- package/dist/safe-edit.d.ts +7 -0
- package/dist/safe-edit.js +163 -37
- package/dist/schemas.js +48 -1
- package/dist/server.js +51 -0
- package/dist/shared.d.ts +3 -2
- package/dist/shared.js +2 -0
- package/dist/status-events.js +9 -6
- package/dist/store/ace-packed-store.d.ts +3 -2
- package/dist/store/ace-packed-store.js +188 -110
- package/dist/store/bootstrap-store.d.ts +2 -1
- package/dist/store/bootstrap-store.js +102 -83
- package/dist/store/cache-workspace.js +11 -5
- package/dist/store/materializers/context-snapshot-materializer.js +6 -2
- package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
- package/dist/store/materializers/hook-context-materializer.js +11 -21
- package/dist/store/materializers/host-file-materializer.js +6 -0
- package/dist/store/materializers/projection-manager.d.ts +0 -1
- package/dist/store/materializers/projection-manager.js +5 -13
- package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
- package/dist/store/materializers/vericify-projector.d.ts +7 -7
- package/dist/store/materializers/vericify-projector.js +11 -11
- package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
- package/dist/store/repositories/local-model-runtime-repository.js +242 -6
- package/dist/store/repositories/vericify-repository.d.ts +1 -1
- package/dist/store/skills-install.d.ts +4 -0
- package/dist/store/skills-install.js +21 -12
- package/dist/store/state-reader.d.ts +2 -0
- package/dist/store/state-reader.js +20 -0
- package/dist/store/store-artifacts.d.ts +7 -0
- package/dist/store/store-artifacts.js +27 -1
- package/dist/store/store-authority-audit.d.ts +18 -1
- package/dist/store/store-authority-audit.js +115 -5
- package/dist/store/store-snapshot.d.ts +3 -0
- package/dist/store/store-snapshot.js +22 -2
- package/dist/store/workspace-store-paths.d.ts +39 -0
- package/dist/store/workspace-store-paths.js +94 -0
- package/dist/store/write-coordinator.d.ts +65 -0
- package/dist/store/write-coordinator.js +386 -0
- package/dist/todo-state.js +5 -5
- package/dist/tools-agent.d.ts +20 -0
- package/dist/tools-agent.js +789 -25
- package/dist/tools-discovery.js +136 -1
- package/dist/tools-files.d.ts +7 -0
- package/dist/tools-files.js +1002 -11
- package/dist/tools-framework.js +105 -66
- package/dist/tools-handoff.js +2 -2
- package/dist/tools-lifecycle.js +4 -4
- package/dist/tools-memory.js +6 -6
- package/dist/tools-todo.js +2 -2
- package/dist/tracker-adapters.d.ts +1 -1
- package/dist/tracker-adapters.js +13 -18
- package/dist/tracker-sync.js +5 -3
- package/dist/tui/agent-runner.js +3 -1
- package/dist/tui/chat.js +103 -7
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.js +43 -0
- package/dist/tui/index.js +10 -1
- package/dist/tui/layout.d.ts +20 -0
- package/dist/tui/layout.js +31 -1
- package/dist/tui/local-model-contract.d.ts +6 -2
- package/dist/tui/local-model-contract.js +16 -3
- package/dist/tui/ollama.d.ts +8 -1
- package/dist/tui/ollama.js +53 -12
- package/dist/tui/openai-compatible.d.ts +13 -0
- package/dist/tui/openai-compatible.js +305 -5
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +35 -11
- package/dist/vericify-bridge.d.ts +6 -1
- package/dist/vericify-bridge.js +27 -3
- package/dist/workspace-manager.d.ts +30 -3
- package/dist/workspace-manager.js +257 -27
- package/package.json +1 -2
- package/dist/internal-tool-runtime.d.ts +0 -21
- package/dist/internal-tool-runtime.js +0 -136
- package/dist/store/workspace-snapshot.d.ts +0 -26
- package/dist/store/workspace-snapshot.js +0 -107
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { hostname } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { getWorkspaceStorePath } from "./store-snapshot.js";
|
|
6
|
+
import { withStoreWriteQueue } from "./write-queue.js";
|
|
7
|
+
const DEFAULT_WAIT_MS = 15_000;
|
|
8
|
+
const DEFAULT_POLL_INTERVAL_MS = 100;
|
|
9
|
+
const DEFAULT_LEASE_TTL_MS = 30_000;
|
|
10
|
+
const DEFAULT_STALE_GRACE_MS = 1_000;
|
|
11
|
+
export class StoreWriteLeaseBusyError extends Error {
|
|
12
|
+
storePath;
|
|
13
|
+
lockPath;
|
|
14
|
+
waitedMs;
|
|
15
|
+
currentHolder;
|
|
16
|
+
constructor(input) {
|
|
17
|
+
super(input.currentHolder
|
|
18
|
+
? `Store write lease busy at ${input.storePath}; held by pid=${input.currentHolder.pid} hostname=${input.currentHolder.hostname}`
|
|
19
|
+
: `Store write lease busy at ${input.storePath}; no lease metadata available`);
|
|
20
|
+
this.name = "StoreWriteLeaseBusyError";
|
|
21
|
+
this.storePath = input.storePath;
|
|
22
|
+
this.lockPath = input.lockPath;
|
|
23
|
+
this.waitedMs = input.waitedMs;
|
|
24
|
+
this.currentHolder = input.currentHolder;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class StoreSyncWriteBlockedByAsyncLeaseError extends Error {
|
|
28
|
+
code = "STORE_SYNC_WRITE_BLOCKED_BY_ASYNC_LEASE";
|
|
29
|
+
storePath;
|
|
30
|
+
currentAsyncOperationLabels;
|
|
31
|
+
constructor(input) {
|
|
32
|
+
super(`Sync store write blocked for ${input.storePath}; async ownership is already active in this process. Use safeWriteAsync() / appendStatusEventSafe() / createWorkspaceSessionAsync() instead.`);
|
|
33
|
+
this.name = "StoreSyncWriteBlockedByAsyncLeaseError";
|
|
34
|
+
this.storePath = input.storePath;
|
|
35
|
+
this.currentAsyncOperationLabels = input.currentAsyncOperationLabels;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const asyncStoreOwnerships = new Map();
|
|
39
|
+
function leaseDirPath(storePath) {
|
|
40
|
+
return `${storePath}.write.lock`;
|
|
41
|
+
}
|
|
42
|
+
function leaseMetadataPath(lockPath) {
|
|
43
|
+
return join(lockPath, "lease.json");
|
|
44
|
+
}
|
|
45
|
+
function getOperationLabel(options) {
|
|
46
|
+
return options.operation_label?.trim() || undefined;
|
|
47
|
+
}
|
|
48
|
+
function emitLifecycleEvent(options, event) {
|
|
49
|
+
if (options.emit_lifecycle_events === false)
|
|
50
|
+
return;
|
|
51
|
+
const sink = options.lifecycle_sink;
|
|
52
|
+
if (!sink)
|
|
53
|
+
return;
|
|
54
|
+
try {
|
|
55
|
+
sink({
|
|
56
|
+
...event,
|
|
57
|
+
hostname: hostname(),
|
|
58
|
+
pid: process.pid,
|
|
59
|
+
timestamp: new Date().toISOString(),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Lifecycle sinks are observability only; they must not affect lease behavior.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function registerAsyncStoreOwnership(storePath, operationLabel) {
|
|
67
|
+
const current = asyncStoreOwnerships.get(storePath);
|
|
68
|
+
if (!current) {
|
|
69
|
+
asyncStoreOwnerships.set(storePath, {
|
|
70
|
+
count: 1,
|
|
71
|
+
operationLabels: operationLabel ? [operationLabel] : [],
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
current.count += 1;
|
|
76
|
+
if (operationLabel) {
|
|
77
|
+
current.operationLabels.push(operationLabel);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function releaseAsyncStoreOwnership(storePath, operationLabel) {
|
|
81
|
+
const current = asyncStoreOwnerships.get(storePath);
|
|
82
|
+
if (!current)
|
|
83
|
+
return;
|
|
84
|
+
current.count = Math.max(0, current.count - 1);
|
|
85
|
+
if (operationLabel) {
|
|
86
|
+
const index = current.operationLabels.lastIndexOf(operationLabel);
|
|
87
|
+
if (index >= 0) {
|
|
88
|
+
current.operationLabels.splice(index, 1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (current.count === 0) {
|
|
92
|
+
asyncStoreOwnerships.delete(storePath);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function getAsyncStoreOwnership(storePath) {
|
|
96
|
+
const current = asyncStoreOwnerships.get(storePath);
|
|
97
|
+
if (!current)
|
|
98
|
+
return undefined;
|
|
99
|
+
return {
|
|
100
|
+
count: current.count,
|
|
101
|
+
operationLabels: [...current.operationLabels],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function sleepSync(ms) {
|
|
105
|
+
if (ms <= 0)
|
|
106
|
+
return;
|
|
107
|
+
const shared = new SharedArrayBuffer(4);
|
|
108
|
+
const view = new Int32Array(shared);
|
|
109
|
+
Atomics.wait(view, 0, 0, ms);
|
|
110
|
+
}
|
|
111
|
+
function sleepAsync(ms) {
|
|
112
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
113
|
+
}
|
|
114
|
+
function normalizeLeaseRecord(record) {
|
|
115
|
+
if (!record)
|
|
116
|
+
return undefined;
|
|
117
|
+
if (typeof record.token !== "string" ||
|
|
118
|
+
typeof record.storePath !== "string" ||
|
|
119
|
+
typeof record.lockPath !== "string" ||
|
|
120
|
+
typeof record.metadataPath !== "string" ||
|
|
121
|
+
typeof record.pid !== "number" ||
|
|
122
|
+
typeof record.hostname !== "string" ||
|
|
123
|
+
typeof record.acquiredAt !== "string" ||
|
|
124
|
+
typeof record.expiresAt !== "string" ||
|
|
125
|
+
typeof record.ttlMs !== "number" ||
|
|
126
|
+
typeof record.waitedMs !== "number") {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
token: record.token,
|
|
131
|
+
storePath: record.storePath,
|
|
132
|
+
lockPath: record.lockPath,
|
|
133
|
+
metadataPath: record.metadataPath,
|
|
134
|
+
pid: record.pid,
|
|
135
|
+
hostname: record.hostname,
|
|
136
|
+
agentId: typeof record.agentId === "string" ? record.agentId : undefined,
|
|
137
|
+
operationLabel: typeof record.operationLabel === "string" ? record.operationLabel : undefined,
|
|
138
|
+
acquiredAt: record.acquiredAt,
|
|
139
|
+
expiresAt: record.expiresAt,
|
|
140
|
+
ttlMs: record.ttlMs,
|
|
141
|
+
waitedMs: record.waitedMs,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function readLeaseRecord(lockPath) {
|
|
145
|
+
const metadataPath = leaseMetadataPath(lockPath);
|
|
146
|
+
if (!existsSync(metadataPath))
|
|
147
|
+
return undefined;
|
|
148
|
+
try {
|
|
149
|
+
const parsed = JSON.parse(readFileSync(metadataPath, "utf-8"));
|
|
150
|
+
return normalizeLeaseRecord(parsed);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function leaseIsStale(lease, staleGraceMs) {
|
|
157
|
+
const expiresAt = Date.parse(lease.expiresAt);
|
|
158
|
+
if (!Number.isFinite(expiresAt))
|
|
159
|
+
return true;
|
|
160
|
+
return Date.now() > expiresAt + staleGraceMs;
|
|
161
|
+
}
|
|
162
|
+
function canRecoverStaleLease(lease, staleGraceMs) {
|
|
163
|
+
if (!leaseIsStale(lease, staleGraceMs))
|
|
164
|
+
return false;
|
|
165
|
+
if (lease.hostname !== hostname())
|
|
166
|
+
return false;
|
|
167
|
+
try {
|
|
168
|
+
process.kill(lease.pid, 0);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return error instanceof Error && "code" in error && error.code === "ESRCH";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function createLeaseRecord(storePath, lockPath, options, waitedMs) {
|
|
176
|
+
const ttlMs = options.lease_ttl_ms ?? DEFAULT_LEASE_TTL_MS;
|
|
177
|
+
const acquiredAtMs = Date.now();
|
|
178
|
+
const acquiredAt = new Date(acquiredAtMs).toISOString();
|
|
179
|
+
return {
|
|
180
|
+
token: randomUUID(),
|
|
181
|
+
storePath,
|
|
182
|
+
lockPath,
|
|
183
|
+
metadataPath: leaseMetadataPath(lockPath),
|
|
184
|
+
pid: process.pid,
|
|
185
|
+
hostname: hostname(),
|
|
186
|
+
agentId: options.agent_id,
|
|
187
|
+
operationLabel: options.operation_label,
|
|
188
|
+
acquiredAt,
|
|
189
|
+
expiresAt: new Date(acquiredAtMs + ttlMs).toISOString(),
|
|
190
|
+
ttlMs,
|
|
191
|
+
waitedMs,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function tryAcquireLeaseSync(storePath, lockPath, options, waitedMs) {
|
|
195
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
196
|
+
mkdirSync(lockPath, { recursive: false });
|
|
197
|
+
const lease = createLeaseRecord(storePath, lockPath, options, waitedMs);
|
|
198
|
+
writeFileSync(lease.metadataPath, JSON.stringify({ kind: "store_write_lease", ...lease }, null, 2), "utf-8");
|
|
199
|
+
return lease;
|
|
200
|
+
}
|
|
201
|
+
async function tryAcquireLeaseAsync(storePath, lockPath, options, waitedMs) {
|
|
202
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
203
|
+
mkdirSync(lockPath, { recursive: false });
|
|
204
|
+
const lease = createLeaseRecord(storePath, lockPath, options, waitedMs);
|
|
205
|
+
writeFileSync(lease.metadataPath, JSON.stringify({ kind: "store_write_lease", ...lease }, null, 2), "utf-8");
|
|
206
|
+
return lease;
|
|
207
|
+
}
|
|
208
|
+
function releaseLease(lease) {
|
|
209
|
+
const current = readLeaseRecord(lease.lockPath);
|
|
210
|
+
if (!current) {
|
|
211
|
+
rmSync(lease.lockPath, { recursive: true, force: true });
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (current.token !== lease.token) {
|
|
215
|
+
throw new Error(`Store write lease token mismatch while releasing ${lease.storePath}; refusing to remove a different holder's lock`);
|
|
216
|
+
}
|
|
217
|
+
rmSync(lease.lockPath, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
async function acquireLeaseAsync(storePath, options) {
|
|
220
|
+
const lockPath = leaseDirPath(storePath);
|
|
221
|
+
const waitMs = options.wait_ms ?? DEFAULT_WAIT_MS;
|
|
222
|
+
const pollIntervalMs = options.poll_interval_ms ?? DEFAULT_POLL_INTERVAL_MS;
|
|
223
|
+
const staleGraceMs = options.stale_grace_ms ?? DEFAULT_STALE_GRACE_MS;
|
|
224
|
+
const startedAt = Date.now();
|
|
225
|
+
while (Date.now() - startedAt <= waitMs) {
|
|
226
|
+
try {
|
|
227
|
+
return await tryAcquireLeaseAsync(storePath, lockPath, options, Date.now() - startedAt);
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
const isBusy = error instanceof Error &&
|
|
231
|
+
"code" in error &&
|
|
232
|
+
error.code === "EEXIST";
|
|
233
|
+
if (!isBusy)
|
|
234
|
+
throw error;
|
|
235
|
+
const current = readLeaseRecord(lockPath);
|
|
236
|
+
if (current && canRecoverStaleLease(current, staleGraceMs)) {
|
|
237
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
await sleepAsync(Math.min(pollIntervalMs, waitMs));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const currentHolder = readLeaseRecord(lockPath);
|
|
244
|
+
emitLifecycleEvent(options, {
|
|
245
|
+
event: "store-write-blocked",
|
|
246
|
+
storePath,
|
|
247
|
+
operationLabel: getOperationLabel(options),
|
|
248
|
+
waitedMs: Date.now() - startedAt,
|
|
249
|
+
currentHolder,
|
|
250
|
+
asyncOwnership: getAsyncStoreOwnership(storePath),
|
|
251
|
+
});
|
|
252
|
+
throw new StoreWriteLeaseBusyError({
|
|
253
|
+
storePath,
|
|
254
|
+
lockPath,
|
|
255
|
+
waitedMs: Date.now() - startedAt,
|
|
256
|
+
currentHolder,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
function acquireLeaseSync(storePath, options) {
|
|
260
|
+
const lockPath = leaseDirPath(storePath);
|
|
261
|
+
const waitMs = options.wait_ms ?? DEFAULT_WAIT_MS;
|
|
262
|
+
const pollIntervalMs = options.poll_interval_ms ?? DEFAULT_POLL_INTERVAL_MS;
|
|
263
|
+
const staleGraceMs = options.stale_grace_ms ?? DEFAULT_STALE_GRACE_MS;
|
|
264
|
+
const startedAt = Date.now();
|
|
265
|
+
while (Date.now() - startedAt <= waitMs) {
|
|
266
|
+
try {
|
|
267
|
+
return tryAcquireLeaseSync(storePath, lockPath, options, Date.now() - startedAt);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
const isBusy = error instanceof Error &&
|
|
271
|
+
"code" in error &&
|
|
272
|
+
error.code === "EEXIST";
|
|
273
|
+
if (!isBusy)
|
|
274
|
+
throw error;
|
|
275
|
+
const current = readLeaseRecord(lockPath);
|
|
276
|
+
if (current && canRecoverStaleLease(current, staleGraceMs)) {
|
|
277
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
sleepSync(Math.min(pollIntervalMs, waitMs));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const currentHolder = readLeaseRecord(lockPath);
|
|
284
|
+
emitLifecycleEvent(options, {
|
|
285
|
+
event: "store-write-blocked",
|
|
286
|
+
storePath,
|
|
287
|
+
operationLabel: getOperationLabel(options),
|
|
288
|
+
waitedMs: Date.now() - startedAt,
|
|
289
|
+
currentHolder,
|
|
290
|
+
asyncOwnership: getAsyncStoreOwnership(storePath),
|
|
291
|
+
});
|
|
292
|
+
throw new StoreWriteLeaseBusyError({
|
|
293
|
+
storePath,
|
|
294
|
+
lockPath,
|
|
295
|
+
waitedMs: Date.now() - startedAt,
|
|
296
|
+
currentHolder,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
export async function withStoreWriteCoordinator(storePath, fn, options = {}) {
|
|
300
|
+
const operationLabel = getOperationLabel(options);
|
|
301
|
+
registerAsyncStoreOwnership(storePath, operationLabel);
|
|
302
|
+
emitLifecycleEvent(options, {
|
|
303
|
+
event: "store-write-requested",
|
|
304
|
+
storePath,
|
|
305
|
+
operationLabel,
|
|
306
|
+
asyncOwnership: getAsyncStoreOwnership(storePath),
|
|
307
|
+
});
|
|
308
|
+
try {
|
|
309
|
+
return await withStoreWriteQueue(storePath, async () => {
|
|
310
|
+
emitLifecycleEvent(options, {
|
|
311
|
+
event: "store-write-accepted",
|
|
312
|
+
storePath,
|
|
313
|
+
operationLabel,
|
|
314
|
+
asyncOwnership: getAsyncStoreOwnership(storePath),
|
|
315
|
+
});
|
|
316
|
+
const lease = await acquireLeaseAsync(storePath, options);
|
|
317
|
+
emitLifecycleEvent(options, {
|
|
318
|
+
event: "store-write-started",
|
|
319
|
+
storePath,
|
|
320
|
+
operationLabel,
|
|
321
|
+
waitedMs: lease.waitedMs,
|
|
322
|
+
currentHolder: lease,
|
|
323
|
+
asyncOwnership: getAsyncStoreOwnership(storePath),
|
|
324
|
+
});
|
|
325
|
+
try {
|
|
326
|
+
const result = await fn();
|
|
327
|
+
emitLifecycleEvent(options, {
|
|
328
|
+
event: "store-write-committed",
|
|
329
|
+
storePath,
|
|
330
|
+
operationLabel,
|
|
331
|
+
waitedMs: lease.waitedMs,
|
|
332
|
+
currentHolder: lease,
|
|
333
|
+
asyncOwnership: getAsyncStoreOwnership(storePath),
|
|
334
|
+
});
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
emitLifecycleEvent(options, {
|
|
339
|
+
event: "store-write-failed",
|
|
340
|
+
storePath,
|
|
341
|
+
operationLabel,
|
|
342
|
+
waitedMs: lease.waitedMs,
|
|
343
|
+
currentHolder: lease,
|
|
344
|
+
error: error instanceof Error ? error.message : String(error),
|
|
345
|
+
asyncOwnership: getAsyncStoreOwnership(storePath),
|
|
346
|
+
});
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
releaseLease(lease);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
finally {
|
|
355
|
+
releaseAsyncStoreOwnership(storePath, operationLabel);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
export function withStoreWriteCoordinatorSync(storePath, fn, options = {}) {
|
|
359
|
+
const operationLabel = getOperationLabel(options);
|
|
360
|
+
const asyncOwnership = getAsyncStoreOwnership(storePath);
|
|
361
|
+
if (asyncOwnership && asyncOwnership.count > 0) {
|
|
362
|
+
emitLifecycleEvent(options, {
|
|
363
|
+
event: "store-write-blocked",
|
|
364
|
+
storePath,
|
|
365
|
+
operationLabel,
|
|
366
|
+
asyncOwnership,
|
|
367
|
+
});
|
|
368
|
+
throw new StoreSyncWriteBlockedByAsyncLeaseError({
|
|
369
|
+
storePath,
|
|
370
|
+
currentAsyncOperationLabels: asyncOwnership.operationLabels.length > 0
|
|
371
|
+
? asyncOwnership.operationLabels
|
|
372
|
+
: ["unknown async store writer"],
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
const lease = acquireLeaseSync(storePath, options);
|
|
376
|
+
try {
|
|
377
|
+
return fn();
|
|
378
|
+
}
|
|
379
|
+
finally {
|
|
380
|
+
releaseLease(lease);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
export function resolveStoreWriteLeasePath(workspaceRoot) {
|
|
384
|
+
return `${getWorkspaceStorePath(workspaceRoot)}.write.lock`;
|
|
385
|
+
}
|
|
386
|
+
//# sourceMappingURL=write-coordinator.js.map
|
package/dist/todo-state.js
CHANGED
|
@@ -5,7 +5,7 @@ import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
|
5
5
|
import { TodoRepository } from "./store/repositories/todo-repository.js";
|
|
6
6
|
import { getWorkspaceStorePath, readStoreJsonSync, storeExistsSync } from "./store/store-snapshot.js";
|
|
7
7
|
import { operationalArtifactVirtualPath } from "./store/store-artifacts.js";
|
|
8
|
-
import {
|
|
8
|
+
import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
|
|
9
9
|
import { isReadError, slugify } from "./shared.js";
|
|
10
10
|
const TODO_MARKDOWN_REL = `${ACE_TASKS_ROOT_REL}/todo.md`;
|
|
11
11
|
const TODO_STATE_REL = "agent-state/todo-state.json";
|
|
@@ -263,7 +263,7 @@ async function mirrorTodoStateToStore(root, state) {
|
|
|
263
263
|
const storePath = getWorkspaceStorePath(root);
|
|
264
264
|
if (!existsSync(storePath))
|
|
265
265
|
return;
|
|
266
|
-
await
|
|
266
|
+
await withStoreWriteCoordinator(storePath, async () => {
|
|
267
267
|
const store = await openStore(storePath);
|
|
268
268
|
try {
|
|
269
269
|
const nextIds = state.order.filter((id) => Boolean(state.nodes[id]));
|
|
@@ -294,14 +294,14 @@ async function mirrorTodoStateToStore(root, state) {
|
|
|
294
294
|
finally {
|
|
295
295
|
await store.close();
|
|
296
296
|
}
|
|
297
|
-
});
|
|
297
|
+
}, { operation_label: "syncTodoStateSafe" });
|
|
298
298
|
}
|
|
299
299
|
async function writeStateStoreBacked(root, state) {
|
|
300
300
|
const storePath = getWorkspaceStorePath(root);
|
|
301
301
|
if (!existsSync(storePath)) {
|
|
302
302
|
return writeState(state);
|
|
303
303
|
}
|
|
304
|
-
return
|
|
304
|
+
return withStoreWriteCoordinator(storePath, async () => {
|
|
305
305
|
const store = await openStore(storePath);
|
|
306
306
|
try {
|
|
307
307
|
const repo = new TodoRepository(store);
|
|
@@ -324,7 +324,7 @@ async function writeStateStoreBacked(root, state) {
|
|
|
324
324
|
finally {
|
|
325
325
|
await store.close();
|
|
326
326
|
}
|
|
327
|
-
});
|
|
327
|
+
}, { operation_label: "syncTodoStateSafe" });
|
|
328
328
|
}
|
|
329
329
|
function scheduleTodoMirror(root, state) {
|
|
330
330
|
const snapshot = JSON.parse(JSON.stringify(state));
|
package/dist/tools-agent.d.ts
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
* Agent, skill, kernel, and task-pack tool registrations.
|
|
3
3
|
*/
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { type BridgeResult } from "./model-bridge.js";
|
|
6
|
+
import { proposePlan as proposePlanImpl, validatePlan as validatePlanImpl, loadAcceptanceTraceContract as loadAcceptanceTraceContractImpl } from "./plan-proposal.js";
|
|
7
|
+
import { type TaskPlan, type TaskStep } from "./orchestrator-supervisor.js";
|
|
5
8
|
export type { TaskPlan, TaskStep } from "./orchestrator-supervisor.js";
|
|
9
|
+
export declare function formatStepTaskForBridge(step: Pick<TaskStep, "task" | "upstream_outputs">): string;
|
|
10
|
+
type IntentVerificationOutcome = "ok" | "revisit_step" | "replan_required";
|
|
11
|
+
type IntentDriftReasonCode = "contract_missing" | "contract_invalid" | "bridge_output_malformed_json" | "role_drift_ui_off_topic" | "coder_artifact_stub" | "qa_rewrote_artifact" | "artifact_mismatch" | "forbidden_pattern" | "required_evidence_missing";
|
|
12
|
+
interface IntentVerificationResult {
|
|
13
|
+
outcome: IntentVerificationOutcome;
|
|
14
|
+
reason: string;
|
|
15
|
+
reason_code?: IntentDriftReasonCode;
|
|
16
|
+
uncovered_clauses?: string[];
|
|
17
|
+
}
|
|
18
|
+
export { loadAcceptanceTraceContractImpl as loadAcceptanceTraceContract };
|
|
19
|
+
export { proposePlanImpl as proposePlan, validatePlanImpl as validatePlan };
|
|
20
|
+
export declare function verifyIntentAgainstContract(input: {
|
|
21
|
+
plan: TaskPlan;
|
|
22
|
+
step: TaskStep;
|
|
23
|
+
result: BridgeResult;
|
|
24
|
+
intent_contract: unknown;
|
|
25
|
+
}): IntentVerificationResult;
|
|
6
26
|
export declare function registerAgentTools(server: McpServer): void;
|
|
7
27
|
//# sourceMappingURL=tools-agent.d.ts.map
|