@voybio/ace-swarm 0.2.4 → 2.4.0
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 +11 -1
- package/README.md +20 -13
- package/assets/.agents/skills/eval-harness/SKILL.md +14 -0
- package/assets/.agents/skills/handoff-lint/SKILL.md +14 -0
- package/assets/.agents/skills/incident-commander/SKILL.md +14 -0
- package/assets/.agents/skills/memory-curator/SKILL.md +14 -0
- package/assets/.agents/skills/release-sentry/SKILL.md +14 -0
- package/assets/.agents/skills/risk-quant/SKILL.md +14 -0
- package/assets/.agents/skills/schema-forge/SKILL.md +14 -0
- package/assets/.agents/skills/state-auditor/SKILL.md +14 -0
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/MODULES/gates/gate-correctness.json +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/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
- package/assets/agent-state/STATUS.md +2 -2
- package/assets/scripts/ace-hook-dispatch.mjs +70 -6
- package/assets/scripts/render-mcp-configs.sh +19 -5
- package/dist/ace-context.js +22 -1
- package/dist/ace-server-instructions.js +3 -3
- package/dist/ace-state-resolver.js +5 -3
- package/dist/astgrep-index.d.ts +9 -1
- package/dist/astgrep-index.js +14 -3
- package/dist/cli.js +52 -20
- 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 +288 -0
- package/dist/helpers/drift.d.ts +13 -0
- package/dist/helpers/drift.js +45 -0
- package/dist/helpers/path-utils.d.ts +17 -0
- package/dist/helpers/path-utils.js +104 -0
- package/dist/helpers/store-resolution.d.ts +19 -0
- package/dist/helpers/store-resolution.js +301 -0
- package/dist/helpers/workspace-root.d.ts +3 -0
- package/dist/helpers/workspace-root.js +80 -0
- package/dist/helpers.d.ts +8 -123
- package/dist/helpers.js +8 -1747
- package/dist/job-scheduler.js +3 -3
- package/dist/local-model-runtime.js +12 -1
- package/dist/model-bridge.d.ts +7 -0
- package/dist/model-bridge.js +75 -5
- package/dist/orchestrator-supervisor.d.ts +14 -0
- package/dist/orchestrator-supervisor.js +72 -1
- 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 +14 -0
- package/dist/runtime-executor.js +669 -171
- package/dist/runtime-profile.d.ts +32 -0
- package/dist/runtime-profile.js +89 -13
- package/dist/runtime-tool-specs.d.ts +21 -0
- package/dist/runtime-tool-specs.js +78 -3
- package/dist/safe-edit.d.ts +7 -0
- package/dist/safe-edit.js +163 -37
- package/dist/schemas.js +19 -0
- package/dist/shared.d.ts +2 -2
- 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 +1 -1
- package/dist/store/bootstrap-store.js +94 -81
- package/dist/store/cache-workspace.d.ts +22 -0
- package/dist/store/cache-workspace.js +149 -0
- package/dist/store/materializers/context-snapshot-materializer.js +6 -7
- 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/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.js +319 -34
- package/dist/tools-discovery.js +1 -1
- package/dist/tools-files.d.ts +7 -0
- package/dist/tools-files.js +299 -10
- package/dist/tools-framework.js +107 -27
- 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/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/vericify-bridge.d.ts +5 -0
- 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
|
@@ -58,6 +58,25 @@ export class StoreStateReader {
|
|
|
58
58
|
r.localModelRuntime.listRuntimeStatuses(),
|
|
59
59
|
r.vericify.list(),
|
|
60
60
|
]);
|
|
61
|
+
// Phase 2: fetch transition log for the live session (needs runtimeStatuses from phase 1)
|
|
62
|
+
const liveStatus = runtimeStatuses.find((s) => {
|
|
63
|
+
try {
|
|
64
|
+
process.kill(s.process_id, 0);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
let recentTransitions = [];
|
|
72
|
+
if (liveStatus?.session_id) {
|
|
73
|
+
try {
|
|
74
|
+
recentTransitions = await r.localModelRuntime.getTransitionLog(liveStatus.session_id, 15);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
recentTransitions = [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
61
80
|
return {
|
|
62
81
|
handoffs,
|
|
63
82
|
todos,
|
|
@@ -70,6 +89,7 @@ export class StoreStateReader {
|
|
|
70
89
|
providers,
|
|
71
90
|
runtimeStatuses,
|
|
72
91
|
posts,
|
|
92
|
+
recentTransitions,
|
|
73
93
|
snapped_at: Date.now(),
|
|
74
94
|
};
|
|
75
95
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type StoreWriteCoordinatorOptions } from "./write-coordinator.js";
|
|
1
2
|
export declare const OPERATIONAL_ARTIFACT_REL_PATHS: Set<string>;
|
|
2
3
|
export declare function operationalArtifactKey(relPath: string): string;
|
|
3
4
|
export declare function isOperationalArtifactPath(relPath: string): boolean;
|
|
@@ -9,4 +10,10 @@ export declare function writeStoreBlobsSync(workspaceRoot: string, blobs: Readon
|
|
|
9
10
|
}>): string[];
|
|
10
11
|
export declare function writeStoreBlobSync(workspaceRoot: string, key: string, content: string): string;
|
|
11
12
|
export declare function writeOperationalArtifactSync(workspaceRoot: string, relPath: string, content: string): string;
|
|
13
|
+
export declare function writeStoreBlobsQueued(workspaceRoot: string, blobs: ReadonlyArray<{
|
|
14
|
+
key: string;
|
|
15
|
+
content: string;
|
|
16
|
+
}>, options?: StoreWriteCoordinatorOptions): Promise<string[]>;
|
|
17
|
+
export declare function writeStoreBlobQueued(workspaceRoot: string, key: string, content: string, options?: StoreWriteCoordinatorOptions): Promise<string>;
|
|
18
|
+
export declare function writeOperationalArtifactQueued(workspaceRoot: string, relPath: string, content: string, options?: StoreWriteCoordinatorOptions): Promise<string>;
|
|
12
19
|
//# sourceMappingURL=store-artifacts.d.ts.map
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { openStore } from "./ace-packed-store.js";
|
|
4
|
+
import { getWorkspaceStorePath, invalidateStoreSnapshotCache, readStoreBlobSync, toVirtualStorePath, } from "./store-snapshot.js";
|
|
5
|
+
import { withStoreWriteCoordinator } from "./write-coordinator.js";
|
|
4
6
|
import { HEADER_SIZE, HEADER_SIZE_V1, STORE_VERSION, MAGIC } from "./types.js";
|
|
5
7
|
// ── v2 header constants (mirrors ace-packed-store.ts) ─────────────────────────
|
|
6
8
|
const H_KV_IDX_OFF = 16;
|
|
@@ -191,6 +193,7 @@ export function writeStoreBlobsSync(workspaceRoot, blobs) {
|
|
|
191
193
|
const tmpPath = `${storePath}.${process.pid}.${Date.now()}.tmp`;
|
|
192
194
|
writeFileSync(tmpPath, output);
|
|
193
195
|
renameSync(tmpPath, storePath);
|
|
196
|
+
invalidateStoreSnapshotCache(storePath);
|
|
194
197
|
return blobs.map(({ key }) => toVirtualStorePath(storePath, key));
|
|
195
198
|
}
|
|
196
199
|
export function writeStoreBlobSync(workspaceRoot, key, content) {
|
|
@@ -199,4 +202,27 @@ export function writeStoreBlobSync(workspaceRoot, key, content) {
|
|
|
199
202
|
export function writeOperationalArtifactSync(workspaceRoot, relPath, content) {
|
|
200
203
|
return writeStoreBlobSync(workspaceRoot, operationalArtifactKey(relPath), content);
|
|
201
204
|
}
|
|
205
|
+
export async function writeStoreBlobsQueued(workspaceRoot, blobs, options = {}) {
|
|
206
|
+
const storePath = getWorkspaceStorePath(workspaceRoot);
|
|
207
|
+
return withStoreWriteCoordinator(storePath, async () => {
|
|
208
|
+
const store = await openStore(storePath);
|
|
209
|
+
try {
|
|
210
|
+
for (const { key, content } of blobs) {
|
|
211
|
+
await store.setBlob(key, content);
|
|
212
|
+
}
|
|
213
|
+
await store.commit();
|
|
214
|
+
return blobs.map(({ key }) => toVirtualStorePath(storePath, key));
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
await store.close();
|
|
218
|
+
}
|
|
219
|
+
}, { ...options, operation_label: options.operation_label ?? "writeStoreBlobsQueued" });
|
|
220
|
+
}
|
|
221
|
+
export async function writeStoreBlobQueued(workspaceRoot, key, content, options = {}) {
|
|
222
|
+
const [path] = await writeStoreBlobsQueued(workspaceRoot, [{ key, content }], options);
|
|
223
|
+
return path;
|
|
224
|
+
}
|
|
225
|
+
export async function writeOperationalArtifactQueued(workspaceRoot, relPath, content, options = {}) {
|
|
226
|
+
return writeStoreBlobQueued(workspaceRoot, operationalArtifactKey(relPath), content, options);
|
|
227
|
+
}
|
|
202
228
|
//# sourceMappingURL=store-artifacts.js.map
|
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
export declare const STORE_AUTHORITY_AUDIT_REPORT_REL_PATH = "agent-state/STORE_AUTHORITY_AUDIT.md";
|
|
2
2
|
export type StoreAuthorityClassification = "canonical knowledge artifact" | "canonical runtime state" | "projection only";
|
|
3
|
+
export type StoreAuthorityTopologyClassification = "runtime_hot_path_sync_writer" | "async_handler_sync_writer" | "allowed_offline_sync_writer" | "store_backed_possible" | "non_blocking";
|
|
4
|
+
export type StoreAuthorityCallName = "safeWrite" | "safeWriteWorkspaceFile" | "safeWriteReport" | "withStoreWriteCoordinatorSync" | "writeStoreBlobSync" | "writeStoreBlobsSync" | "appendStatusEvent" | "createWorkspaceSession";
|
|
5
|
+
export interface StoreAuthorityTopologyFlags {
|
|
6
|
+
runtime_hot_path: boolean;
|
|
7
|
+
async_handler: boolean;
|
|
8
|
+
store_backed_possible: boolean;
|
|
9
|
+
allowed_offline_sync: boolean;
|
|
10
|
+
must_migrate_to_async: boolean;
|
|
11
|
+
}
|
|
3
12
|
export interface StoreAuthoritySafeWriteSite {
|
|
4
|
-
call:
|
|
13
|
+
call: StoreAuthorityCallName;
|
|
5
14
|
classification: StoreAuthorityClassification;
|
|
6
15
|
file: string;
|
|
7
16
|
line: number;
|
|
8
17
|
column: number;
|
|
18
|
+
enclosing_function?: string;
|
|
19
|
+
enclosing_function_is_async: boolean;
|
|
9
20
|
target_expression: string;
|
|
10
21
|
target_path?: string;
|
|
11
22
|
fallback_keys: string[];
|
|
12
23
|
bypasses_acepack: boolean;
|
|
13
24
|
regenerable_from_store: boolean;
|
|
25
|
+
topology: StoreAuthorityTopologyFlags;
|
|
26
|
+
topology_classification: StoreAuthorityTopologyClassification;
|
|
27
|
+
blocking: boolean;
|
|
28
|
+
failure_condition: string;
|
|
14
29
|
note: string;
|
|
15
30
|
}
|
|
16
31
|
export interface StoreAuthorityAuditResult {
|
|
17
32
|
bypassing_sites: StoreAuthoritySafeWriteSite[];
|
|
33
|
+
blocking_sites: StoreAuthoritySafeWriteSite[];
|
|
18
34
|
classifications: Record<StoreAuthorityClassification, StoreAuthoritySafeWriteSite[]>;
|
|
19
35
|
generated_at: string;
|
|
36
|
+
topology_failure: boolean;
|
|
20
37
|
non_regenerable_projections: StoreAuthoritySafeWriteSite[];
|
|
21
38
|
report_path: string;
|
|
22
39
|
runtime_files_without_store_fallback_keys: StoreAuthoritySafeWriteSite[];
|
|
@@ -7,8 +7,32 @@ import { OPERATIONAL_ARTIFACT_REL_PATHS, operationalArtifactKey } from "./store-
|
|
|
7
7
|
export const STORE_AUTHORITY_AUDIT_REPORT_REL_PATH = "agent-state/STORE_AUTHORITY_AUDIT.md";
|
|
8
8
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
9
9
|
const SRC_ROOT = resolve(PACKAGE_ROOT, "src");
|
|
10
|
-
const TARGET_CALLS = new Set([
|
|
10
|
+
const TARGET_CALLS = new Set([
|
|
11
|
+
"safeWrite",
|
|
12
|
+
"safeWriteWorkspaceFile",
|
|
13
|
+
"safeWriteReport",
|
|
14
|
+
"withStoreWriteCoordinatorSync",
|
|
15
|
+
"writeStoreBlobSync",
|
|
16
|
+
"writeStoreBlobsSync",
|
|
17
|
+
"appendStatusEvent",
|
|
18
|
+
"createWorkspaceSession",
|
|
19
|
+
]);
|
|
11
20
|
const SKIPPED_INTERNAL_FUNCTIONS = new Set(["safeWriteWorkspaceFile", "safeWriteReport"]);
|
|
21
|
+
const OFFLINE_SYNC_HELPER_SOURCES = new Set([
|
|
22
|
+
"src/helpers/store-resolution.ts",
|
|
23
|
+
"src/store/store-artifacts.ts",
|
|
24
|
+
"src/store/write-coordinator.ts",
|
|
25
|
+
"src/runtime-executor.ts",
|
|
26
|
+
"src/status-events.ts",
|
|
27
|
+
"src/run-ledger.ts",
|
|
28
|
+
"src/handoff-registry.ts",
|
|
29
|
+
"src/workspace-manager.ts",
|
|
30
|
+
"src/job-scheduler.ts",
|
|
31
|
+
"src/astgrep-index.ts",
|
|
32
|
+
"src/index-store.ts",
|
|
33
|
+
"src/tools-files.ts",
|
|
34
|
+
]);
|
|
35
|
+
const TOOL_RUNTIME_SOURCE_HINT = /(?:^|\/)(?:tools-[^/]+|tools-framework|runtime-[^/]+|tracker-[^/]+|workspace-manager|status-events|job-scheduler|run-ledger|runtime-profile|handoff-registry|astgrep-index)\.ts$/;
|
|
12
36
|
const TASK_KEY_TO_FILE = {
|
|
13
37
|
todo: "todo.md",
|
|
14
38
|
role_tasks: "role_tasks.md",
|
|
@@ -74,6 +98,9 @@ function functionName(node) {
|
|
|
74
98
|
return undefined;
|
|
75
99
|
return undefined;
|
|
76
100
|
}
|
|
101
|
+
function hasAsyncModifier(node) {
|
|
102
|
+
return node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
103
|
+
}
|
|
77
104
|
function callName(node) {
|
|
78
105
|
const expr = node.expression;
|
|
79
106
|
if (ts.isIdentifier(expr))
|
|
@@ -82,6 +109,70 @@ function callName(node) {
|
|
|
82
109
|
return expr.name.text;
|
|
83
110
|
return undefined;
|
|
84
111
|
}
|
|
112
|
+
function isRuntimeHotPathSource(relativeSource) {
|
|
113
|
+
return TOOL_RUNTIME_SOURCE_HINT.test(relativeSource);
|
|
114
|
+
}
|
|
115
|
+
function isOfflineSyncAllowedSource(relativeSource) {
|
|
116
|
+
return OFFLINE_SYNC_HELPER_SOURCES.has(relativeSource);
|
|
117
|
+
}
|
|
118
|
+
function classifyTopology(sourcePath, currentFunction, currentFunctionIsAsync, callName, resolved) {
|
|
119
|
+
const relativeSource = normalizeAuditPath(relative(PACKAGE_ROOT, sourcePath));
|
|
120
|
+
const runtimeHotPath = isRuntimeHotPathSource(relativeSource) ||
|
|
121
|
+
(currentFunction ? /(?:runtime|session|workspace|status|tracker|tool|handler|executor|lifecycle|scheduler|ledger)/i.test(currentFunction) : false) ||
|
|
122
|
+
currentFunctionIsAsync ||
|
|
123
|
+
callName === "createWorkspaceSession" ||
|
|
124
|
+
callName === "appendStatusEvent";
|
|
125
|
+
const asyncHandler = currentFunctionIsAsync &&
|
|
126
|
+
(isServerToolHandlerFromSource(sourcePath, currentFunction) || /(?:handler|tool|callback|command|action|task)/i.test(currentFunction ?? ""));
|
|
127
|
+
const allowedOfflineSync = isOfflineSyncAllowedSource(relativeSource) ||
|
|
128
|
+
(relativeSource === "src/status-events.ts" && currentFunction === "appendStatusEventSafe");
|
|
129
|
+
const storeBackedPossible = callName === "safeWrite" ||
|
|
130
|
+
callName === "safeWriteWorkspaceFile" ||
|
|
131
|
+
callName === "safeWriteReport" ||
|
|
132
|
+
callName === "withStoreWriteCoordinatorSync" ||
|
|
133
|
+
callName === "writeStoreBlobSync" ||
|
|
134
|
+
callName === "writeStoreBlobsSync" ||
|
|
135
|
+
callName === "appendStatusEvent" ||
|
|
136
|
+
callName === "createWorkspaceSession" ||
|
|
137
|
+
resolved.fallbackKeys.length > 0;
|
|
138
|
+
const mustMigrateToAsync = storeBackedPossible && !allowedOfflineSync && (runtimeHotPath || asyncHandler);
|
|
139
|
+
const blocking = runtimeHotPath && mustMigrateToAsync;
|
|
140
|
+
const classification = blocking
|
|
141
|
+
? "runtime_hot_path_sync_writer"
|
|
142
|
+
: asyncHandler && mustMigrateToAsync
|
|
143
|
+
? "async_handler_sync_writer"
|
|
144
|
+
: allowedOfflineSync
|
|
145
|
+
? "allowed_offline_sync_writer"
|
|
146
|
+
: storeBackedPossible
|
|
147
|
+
? "store_backed_possible"
|
|
148
|
+
: "non_blocking";
|
|
149
|
+
return {
|
|
150
|
+
flags: {
|
|
151
|
+
runtime_hot_path: runtimeHotPath,
|
|
152
|
+
async_handler: asyncHandler,
|
|
153
|
+
store_backed_possible: storeBackedPossible,
|
|
154
|
+
allowed_offline_sync: allowedOfflineSync,
|
|
155
|
+
must_migrate_to_async: mustMigrateToAsync,
|
|
156
|
+
},
|
|
157
|
+
classification,
|
|
158
|
+
blocking,
|
|
159
|
+
failure_condition: mustMigrateToAsync
|
|
160
|
+
? "runtime_hot_path && store_backed_possible && !allowed_offline_sync"
|
|
161
|
+
: "no blocking migration required",
|
|
162
|
+
note: blocking
|
|
163
|
+
? "production runtime hot path still reaches a sync store-backed writer"
|
|
164
|
+
: allowedOfflineSync
|
|
165
|
+
? "sync writer is an allowed offline/library-local helper"
|
|
166
|
+
: storeBackedPossible
|
|
167
|
+
? "store-backed sync surface is present but not currently blocking"
|
|
168
|
+
: "non-store topology site",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function isServerToolHandlerFromSource(sourcePath, currentFunction) {
|
|
172
|
+
if (!currentFunction)
|
|
173
|
+
return false;
|
|
174
|
+
return /(?:tools-|runtime-|tracker-|workspace-manager|status-events|job-scheduler|run-ledger|runtime-profile|handoff-registry|astgrep-index)/.test(normalizeAuditPath(relative(PACKAGE_ROOT, sourcePath)));
|
|
175
|
+
}
|
|
85
176
|
function collectResolvableConstants(root, sourceFile, inherited) {
|
|
86
177
|
const constants = new Map(inherited);
|
|
87
178
|
const pending = [];
|
|
@@ -325,18 +416,20 @@ function scanSourceFile(sourcePath) {
|
|
|
325
416
|
const raw = readFileSync(sourcePath, "utf-8");
|
|
326
417
|
const sourceFile = ts.createSourceFile(sourcePath, raw, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
327
418
|
const sites = [];
|
|
328
|
-
const scanNode = (node, inheritedConstants, currentFunction) => {
|
|
419
|
+
const scanNode = (node, inheritedConstants, currentFunction, currentFunctionIsAsync = false) => {
|
|
329
420
|
if (node !== sourceFile && isFunctionLike(node)) {
|
|
330
421
|
const name = functionName(node) ?? currentFunction;
|
|
331
422
|
const constants = collectResolvableConstants(node, sourceFile, inheritedConstants);
|
|
423
|
+
const isAsync = hasAsyncModifier(node);
|
|
332
424
|
if (node.body) {
|
|
333
|
-
ts.forEachChild(node.body, (child) => scanNode(child, constants, name));
|
|
425
|
+
ts.forEachChild(node.body, (child) => scanNode(child, constants, name, isAsync));
|
|
334
426
|
}
|
|
335
427
|
return;
|
|
336
428
|
}
|
|
337
429
|
if (ts.isCallExpression(node)) {
|
|
338
430
|
const name = callName(node);
|
|
339
431
|
if (name && TARGET_CALLS.has(name)) {
|
|
432
|
+
const call = name;
|
|
340
433
|
if (currentFunction && SKIPPED_INTERNAL_FUNCTIONS.has(currentFunction) && name === "safeWrite") {
|
|
341
434
|
return;
|
|
342
435
|
}
|
|
@@ -344,22 +437,29 @@ function scanSourceFile(sourcePath) {
|
|
|
344
437
|
const targetPath = firstArg ? resolveExpression(firstArg, sourceFile, inheritedConstants) : undefined;
|
|
345
438
|
const resolved = classifyTarget(targetPath);
|
|
346
439
|
const location = formatLocation(sourceFile, node);
|
|
440
|
+
const topology = classifyTopology(sourcePath, currentFunction, currentFunctionIsAsync, call, resolved);
|
|
347
441
|
sites.push({
|
|
348
|
-
call
|
|
442
|
+
call,
|
|
349
443
|
classification: resolved.classification,
|
|
350
444
|
file: normalizeAuditPath(relative(PACKAGE_ROOT, sourcePath)),
|
|
351
445
|
line: location.line,
|
|
352
446
|
column: location.column,
|
|
447
|
+
enclosing_function: currentFunction,
|
|
448
|
+
enclosing_function_is_async: currentFunctionIsAsync,
|
|
353
449
|
target_expression: firstArg ? firstArg.getText(sourceFile) : "",
|
|
354
450
|
target_path: resolved.canonicalTarget,
|
|
355
451
|
fallback_keys: resolved.fallbackKeys,
|
|
356
452
|
bypasses_acepack: resolved.bypassesAcepack,
|
|
357
453
|
regenerable_from_store: resolved.regenerableFromStore,
|
|
454
|
+
topology: topology.flags,
|
|
455
|
+
topology_classification: topology.classification,
|
|
456
|
+
blocking: topology.blocking,
|
|
457
|
+
failure_condition: topology.failure_condition,
|
|
358
458
|
note: resolved.note,
|
|
359
459
|
});
|
|
360
460
|
}
|
|
361
461
|
}
|
|
362
|
-
ts.forEachChild(node, (child) => scanNode(child, inheritedConstants, currentFunction));
|
|
462
|
+
ts.forEachChild(node, (child) => scanNode(child, inheritedConstants, currentFunction, currentFunctionIsAsync));
|
|
363
463
|
};
|
|
364
464
|
const fileConstants = collectResolvableConstants(sourceFile, sourceFile, new Map());
|
|
365
465
|
ts.forEachChild(sourceFile, (child) => scanNode(child, fileConstants));
|
|
@@ -377,11 +477,14 @@ export function auditStoreAuthority() {
|
|
|
377
477
|
const classifications = groupByClassification(safeWriteSites);
|
|
378
478
|
const runtimeFilesWithoutStoreFallbackKeys = safeWriteSites.filter((site) => site.bypasses_acepack);
|
|
379
479
|
const nonRegenerableProjections = safeWriteSites.filter((site) => site.classification === "projection only" && !site.regenerable_from_store);
|
|
480
|
+
const blockingSites = safeWriteSites.filter((site) => site.blocking);
|
|
380
481
|
const report_path = STORE_AUTHORITY_AUDIT_REPORT_REL_PATH;
|
|
381
482
|
return {
|
|
382
483
|
bypassing_sites: runtimeFilesWithoutStoreFallbackKeys,
|
|
484
|
+
blocking_sites: blockingSites,
|
|
383
485
|
classifications,
|
|
384
486
|
generated_at: new Date().toISOString(),
|
|
487
|
+
topology_failure: blockingSites.length > 0,
|
|
385
488
|
non_regenerable_projections: nonRegenerableProjections,
|
|
386
489
|
report_path,
|
|
387
490
|
runtime_files_without_store_fallback_keys: runtimeFilesWithoutStoreFallbackKeys,
|
|
@@ -408,6 +511,9 @@ function renderSites(label, sites) {
|
|
|
408
511
|
lines.push(` - target: ${site.target_path ? `\`${site.target_path}\`` : "unresolved"}`);
|
|
409
512
|
lines.push(` - fallback keys: ${site.fallback_keys.length > 0 ? site.fallback_keys.map((key) => `\`${key}\``).join(", ") : "none"}`);
|
|
410
513
|
lines.push(` - classification: \`${site.classification}\``);
|
|
514
|
+
lines.push(` - topology: \`${site.topology_classification}\``);
|
|
515
|
+
lines.push(` - flags: runtime_hot_path=${site.topology.runtime_hot_path}, async_handler=${site.topology.async_handler}, store_backed_possible=${site.topology.store_backed_possible}, allowed_offline_sync=${site.topology.allowed_offline_sync}, must_migrate_to_async=${site.topology.must_migrate_to_async}`);
|
|
516
|
+
lines.push(` - failure condition: ${site.failure_condition}`);
|
|
411
517
|
lines.push(` - note: ${site.note}`);
|
|
412
518
|
}
|
|
413
519
|
return lines;
|
|
@@ -422,12 +528,16 @@ export function renderStoreAuthorityAuditReport(result) {
|
|
|
422
528
|
"",
|
|
423
529
|
"## Summary",
|
|
424
530
|
`- bypassing ACEPACK: ${result.bypassing_sites.length}`,
|
|
531
|
+
`- blocking runtime hot-path writers: ${result.blocking_sites.length}`,
|
|
532
|
+
`- topology failure: ${result.topology_failure ? "yes" : "no"}`,
|
|
425
533
|
`- runtime files without store fallback keys: ${result.runtime_files_without_store_fallback_keys.length}`,
|
|
426
534
|
`- non-regenerable projections: ${result.non_regenerable_projections.length}`,
|
|
427
535
|
`- canonical knowledge artifacts: ${result.classifications["canonical knowledge artifact"].length}`,
|
|
428
536
|
`- canonical runtime state writers: ${result.classifications["canonical runtime state"].length}`,
|
|
429
537
|
`- projection-only writers: ${result.classifications["projection only"].length}`,
|
|
430
538
|
"",
|
|
539
|
+
...renderSites("Blocking Runtime Hot-Path Writers", result.blocking_sites),
|
|
540
|
+
"",
|
|
431
541
|
...renderSites("Writers Bypassing ACEPACK", result.bypassing_sites),
|
|
432
542
|
"",
|
|
433
543
|
...renderSites("Runtime Files Without Store Fallback Keys", result.runtime_files_without_store_fallback_keys),
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export declare function invalidateStoreSnapshotCache(storePath?: string): void;
|
|
2
|
+
export declare function getCanonicalWorkspaceStorePath(workspaceRoot: string): string;
|
|
3
|
+
export declare function getLegacyWorkspaceStorePath(workspaceRoot: string): string;
|
|
1
4
|
export declare function getWorkspaceStorePath(workspaceRoot: string): string;
|
|
2
5
|
export declare function storeExistsSync(workspaceRoot: string): boolean;
|
|
3
6
|
export declare function readStoreBlobSync(workspaceRoot: string, key: string): string | undefined;
|
|
@@ -3,6 +3,13 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { HEADER_SIZE, MAGIC } from "./types.js";
|
|
4
4
|
const DECODE = new TextDecoder();
|
|
5
5
|
let cached;
|
|
6
|
+
export function invalidateStoreSnapshotCache(storePath) {
|
|
7
|
+
if (!cached)
|
|
8
|
+
return;
|
|
9
|
+
if (!storePath || cached.path === storePath) {
|
|
10
|
+
cached = undefined;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
6
13
|
function bytesToUint32(bytes, offset = 0) {
|
|
7
14
|
return new DataView(bytes.buffer, bytes.byteOffset).getUint32(offset, false);
|
|
8
15
|
}
|
|
@@ -47,12 +54,25 @@ function loadSnapshot(storePath) {
|
|
|
47
54
|
return cached;
|
|
48
55
|
}
|
|
49
56
|
catch {
|
|
50
|
-
|
|
57
|
+
invalidateStoreSnapshotCache(storePath);
|
|
58
|
+
return undefined;
|
|
51
59
|
}
|
|
52
60
|
}
|
|
53
|
-
export function
|
|
61
|
+
export function getCanonicalWorkspaceStorePath(workspaceRoot) {
|
|
62
|
+
return resolve(workspaceRoot, "agent-state", "ace-state.ace");
|
|
63
|
+
}
|
|
64
|
+
export function getLegacyWorkspaceStorePath(workspaceRoot) {
|
|
54
65
|
return resolve(workspaceRoot, ".agents", "ACE", "ace-state.ace");
|
|
55
66
|
}
|
|
67
|
+
export function getWorkspaceStorePath(workspaceRoot) {
|
|
68
|
+
const canonicalPath = getCanonicalWorkspaceStorePath(workspaceRoot);
|
|
69
|
+
if (existsSync(canonicalPath))
|
|
70
|
+
return canonicalPath;
|
|
71
|
+
const legacyPath = getLegacyWorkspaceStorePath(workspaceRoot);
|
|
72
|
+
if (existsSync(legacyPath))
|
|
73
|
+
return legacyPath;
|
|
74
|
+
return canonicalPath;
|
|
75
|
+
}
|
|
56
76
|
export function storeExistsSync(workspaceRoot) {
|
|
57
77
|
return existsSync(getWorkspaceStorePath(workspaceRoot));
|
|
58
78
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace-store-paths.ts
|
|
3
|
+
*
|
|
4
|
+
* Central resolver and adoption helper for the ACE canonical/legacy store transition.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - canonical: agent-state/ace-state.ace (new and adopted workspaces)
|
|
8
|
+
* - legacy: .agents/ACE/ace-state.ace (older workspaces; supported input, not a write target)
|
|
9
|
+
*
|
|
10
|
+
* Every command that opens ACEPACK must use this module. No command should
|
|
11
|
+
* assemble .agents/ACE/ace-state.ace by hand.
|
|
12
|
+
*/
|
|
13
|
+
export interface WorkspaceStoreResolution {
|
|
14
|
+
storePath: string;
|
|
15
|
+
canonicalPath: string;
|
|
16
|
+
legacyPath: string;
|
|
17
|
+
mode: "canonical" | "legacy_fallback" | "adopted_legacy" | "dual_store_conflict" | "missing";
|
|
18
|
+
warnings: string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Read-safe, non-mutating resolver. Reports the active store path and whether
|
|
22
|
+
* legacy also exists. Does not copy or seed any files.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveWorkspaceStorePath(workspaceRoot: string): WorkspaceStoreResolution;
|
|
25
|
+
/**
|
|
26
|
+
* Mutating resolver. Use before any command that writes to the store.
|
|
27
|
+
*
|
|
28
|
+
* Adoption behaviour:
|
|
29
|
+
* - canonical only → return canonical
|
|
30
|
+
* - canonical + legacy identical → return canonical with low-severity warning
|
|
31
|
+
* - canonical + legacy differ → return dual_store_conflict (caller decides how to handle)
|
|
32
|
+
* - legacy only → copy legacy to canonical atomically, return adopted_legacy
|
|
33
|
+
* - neither → return missing (caller seeds a fresh store)
|
|
34
|
+
*/
|
|
35
|
+
export declare function ensureCanonicalWorkspaceStore(workspaceRoot: string, options?: {
|
|
36
|
+
operationLabel?: string;
|
|
37
|
+
allowDualStoreConflict?: boolean;
|
|
38
|
+
}): Promise<WorkspaceStoreResolution>;
|
|
39
|
+
//# sourceMappingURL=workspace-store-paths.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace-store-paths.ts
|
|
3
|
+
*
|
|
4
|
+
* Central resolver and adoption helper for the ACE canonical/legacy store transition.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - canonical: agent-state/ace-state.ace (new and adopted workspaces)
|
|
8
|
+
* - legacy: .agents/ACE/ace-state.ace (older workspaces; supported input, not a write target)
|
|
9
|
+
*
|
|
10
|
+
* Every command that opens ACEPACK must use this module. No command should
|
|
11
|
+
* assemble .agents/ACE/ace-state.ace by hand.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readFileSync, copyFileSync, mkdirSync } from "node:fs";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
import { getCanonicalWorkspaceStorePath, getLegacyWorkspaceStorePath, } from "./store-snapshot.js";
|
|
16
|
+
import { withStoreWriteCoordinator } from "./write-coordinator.js";
|
|
17
|
+
/**
|
|
18
|
+
* Read-safe, non-mutating resolver. Reports the active store path and whether
|
|
19
|
+
* legacy also exists. Does not copy or seed any files.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveWorkspaceStorePath(workspaceRoot) {
|
|
22
|
+
const canonicalPath = getCanonicalWorkspaceStorePath(workspaceRoot);
|
|
23
|
+
const legacyPath = getLegacyWorkspaceStorePath(workspaceRoot);
|
|
24
|
+
const hasCanonical = existsSync(canonicalPath);
|
|
25
|
+
const hasLegacy = existsSync(legacyPath);
|
|
26
|
+
const warnings = [];
|
|
27
|
+
if (hasCanonical && hasLegacy) {
|
|
28
|
+
const canonicalBytes = readFileSync(canonicalPath);
|
|
29
|
+
const legacyBytes = readFileSync(legacyPath);
|
|
30
|
+
const identical = canonicalBytes.equals(legacyBytes);
|
|
31
|
+
if (identical) {
|
|
32
|
+
warnings.push(`Legacy store ${legacyPath} is byte-identical to canonical store. Legacy is redundant and can be removed.`);
|
|
33
|
+
return { storePath: canonicalPath, canonicalPath, legacyPath, mode: "canonical", warnings };
|
|
34
|
+
}
|
|
35
|
+
warnings.push(`Dual ACE stores detected. Canonical: ${canonicalPath} | Legacy: ${legacyPath}. They differ — use 'ace repair' to resolve.`);
|
|
36
|
+
return {
|
|
37
|
+
storePath: canonicalPath,
|
|
38
|
+
canonicalPath,
|
|
39
|
+
legacyPath,
|
|
40
|
+
mode: "dual_store_conflict",
|
|
41
|
+
warnings,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (hasCanonical) {
|
|
45
|
+
return { storePath: canonicalPath, canonicalPath, legacyPath, mode: "canonical", warnings };
|
|
46
|
+
}
|
|
47
|
+
if (hasLegacy) {
|
|
48
|
+
return {
|
|
49
|
+
storePath: legacyPath,
|
|
50
|
+
canonicalPath,
|
|
51
|
+
legacyPath,
|
|
52
|
+
mode: "legacy_fallback",
|
|
53
|
+
warnings,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return { storePath: canonicalPath, canonicalPath, legacyPath, mode: "missing", warnings };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Mutating resolver. Use before any command that writes to the store.
|
|
60
|
+
*
|
|
61
|
+
* Adoption behaviour:
|
|
62
|
+
* - canonical only → return canonical
|
|
63
|
+
* - canonical + legacy identical → return canonical with low-severity warning
|
|
64
|
+
* - canonical + legacy differ → return dual_store_conflict (caller decides how to handle)
|
|
65
|
+
* - legacy only → copy legacy to canonical atomically, return adopted_legacy
|
|
66
|
+
* - neither → return missing (caller seeds a fresh store)
|
|
67
|
+
*/
|
|
68
|
+
export async function ensureCanonicalWorkspaceStore(workspaceRoot, options) {
|
|
69
|
+
const label = options?.operationLabel ?? "ensureCanonicalWorkspaceStore";
|
|
70
|
+
const resolution = resolveWorkspaceStorePath(workspaceRoot);
|
|
71
|
+
if (resolution.mode === "legacy_fallback") {
|
|
72
|
+
// Adopt: copy legacy into canonical atomically under write coordinator.
|
|
73
|
+
const { canonicalPath, legacyPath } = resolution;
|
|
74
|
+
await withStoreWriteCoordinator(canonicalPath, async () => {
|
|
75
|
+
// Re-check after acquiring the write lock — another process may have adopted already.
|
|
76
|
+
if (!existsSync(canonicalPath)) {
|
|
77
|
+
mkdirSync(dirname(canonicalPath), { recursive: true });
|
|
78
|
+
copyFileSync(legacyPath, canonicalPath);
|
|
79
|
+
}
|
|
80
|
+
}, { operation_label: `${label}:adopt_legacy` });
|
|
81
|
+
const warnings = [
|
|
82
|
+
`Legacy store adopted: copied ${legacyPath} → ${canonicalPath}. Legacy file retained as fallback.`,
|
|
83
|
+
];
|
|
84
|
+
return {
|
|
85
|
+
storePath: canonicalPath,
|
|
86
|
+
canonicalPath,
|
|
87
|
+
legacyPath,
|
|
88
|
+
mode: "adopted_legacy",
|
|
89
|
+
warnings,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return resolution;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=workspace-store-paths.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface StoreWriteCoordinatorOptions {
|
|
2
|
+
agent_id?: string;
|
|
3
|
+
emit_lifecycle_events?: boolean;
|
|
4
|
+
lifecycle_sink?: (event: StoreWriteLifecycleEvent) => void;
|
|
5
|
+
operation_label?: string;
|
|
6
|
+
wait_ms?: number;
|
|
7
|
+
poll_interval_ms?: number;
|
|
8
|
+
lease_ttl_ms?: number;
|
|
9
|
+
stale_grace_ms?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface StoreWriteLeaseInfo {
|
|
12
|
+
token: string;
|
|
13
|
+
storePath: string;
|
|
14
|
+
lockPath: string;
|
|
15
|
+
metadataPath: string;
|
|
16
|
+
pid: number;
|
|
17
|
+
hostname: string;
|
|
18
|
+
agentId?: string;
|
|
19
|
+
operationLabel?: string;
|
|
20
|
+
acquiredAt: string;
|
|
21
|
+
expiresAt: string;
|
|
22
|
+
ttlMs: number;
|
|
23
|
+
waitedMs: number;
|
|
24
|
+
}
|
|
25
|
+
export type StoreWriteLifecycleEventType = "store-write-requested" | "store-write-accepted" | "store-write-started" | "store-write-committed" | "store-write-blocked" | "store-write-failed";
|
|
26
|
+
export interface StoreWriteLifecycleEvent {
|
|
27
|
+
event: StoreWriteLifecycleEventType;
|
|
28
|
+
storePath: string;
|
|
29
|
+
operationLabel?: string;
|
|
30
|
+
hostname: string;
|
|
31
|
+
pid: number;
|
|
32
|
+
timestamp: string;
|
|
33
|
+
waitedMs?: number;
|
|
34
|
+
currentHolder?: StoreWriteLeaseInfo;
|
|
35
|
+
error?: string;
|
|
36
|
+
asyncOwnership?: {
|
|
37
|
+
count: number;
|
|
38
|
+
operationLabels: string[];
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export declare class StoreWriteLeaseBusyError extends Error {
|
|
42
|
+
readonly storePath: string;
|
|
43
|
+
readonly lockPath: string;
|
|
44
|
+
readonly waitedMs: number;
|
|
45
|
+
readonly currentHolder?: StoreWriteLeaseInfo;
|
|
46
|
+
constructor(input: {
|
|
47
|
+
storePath: string;
|
|
48
|
+
lockPath: string;
|
|
49
|
+
waitedMs: number;
|
|
50
|
+
currentHolder?: StoreWriteLeaseInfo;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export declare class StoreSyncWriteBlockedByAsyncLeaseError extends Error {
|
|
54
|
+
readonly code = "STORE_SYNC_WRITE_BLOCKED_BY_ASYNC_LEASE";
|
|
55
|
+
readonly storePath: string;
|
|
56
|
+
readonly currentAsyncOperationLabels: string[];
|
|
57
|
+
constructor(input: {
|
|
58
|
+
storePath: string;
|
|
59
|
+
currentAsyncOperationLabels: string[];
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export declare function withStoreWriteCoordinator<T>(storePath: string, fn: () => Promise<T>, options?: StoreWriteCoordinatorOptions): Promise<T>;
|
|
63
|
+
export declare function withStoreWriteCoordinatorSync<T>(storePath: string, fn: () => T, options?: StoreWriteCoordinatorOptions): T;
|
|
64
|
+
export declare function resolveStoreWriteLeasePath(workspaceRoot: string): string;
|
|
65
|
+
//# sourceMappingURL=write-coordinator.d.ts.map
|