archgraph-argo 0.10.20 → 0.10.22
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.
|
@@ -99,7 +99,7 @@ You MUST read/write the intent architecture through the tools provided by the AR
|
|
|
99
99
|
16. queryNeo4jGraph: run a read-only Cypher query against the Neo4j structural projection of the intent graph, or request the projection schema with {schema: true}. Use for structural/type-based graph queries (see <GraphQueryGuideline>).
|
|
100
100
|
|
|
101
101
|
<GraphQueryGuideline>
|
|
102
|
-
For structural/type-based graph lookups, use the read-only Neo4j Cypher interface `queryNeo4jGraph` instead of reading the canonical JSON file directly. It never mutates the graph; all writes still go through the mutation tools.
|
|
102
|
+
For structural/type-based graph lookups, use the read-only Neo4j Cypher interface `queryNeo4jGraph` instead of reading the canonical JSON file directly. It never mutates the canonical graph; all writes still go through the mutation tools.
|
|
103
103
|
1. Ask for the projection schema first: call `queryNeo4jGraph` with {schema: true} to learn node labels (ArchitectureGraph, Element, ArchitectureRelationship, View), relationship types (OWNS_ELEMENT, OWNS_RELATIONSHIP, OWNS_VIEW, RELATIONSHIP_SOURCE, RELATIONSHIP_TARGET, ARCHIMATE_RELATES, VIEW_OF, INCLUDES_ELEMENT, INCLUDES_RELATIONSHIP, HAS_SUBDIAGRAM), property keys, and the legal ArchiMate element/relationship type enums (e.g. 'Business Actor', 'Assignment').
|
|
104
104
|
2. Construct a read-only Cypher statement and scope every pattern to the current graph with the server-injected `$graphKey` parameter (the value is filled by the server; the agent only writes the placeholder):
|
|
105
105
|
MATCH (e:Element {graphKey: $graphKey, type: 'Business Actor'}) RETURN e.id, e.name ORDER BY e.name
|
|
@@ -35,20 +35,22 @@ function requireNeo4jDriver() {
|
|
|
35
35
|
return neo4jDriverModule;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function getRepoRoot() {
|
|
39
|
-
return
|
|
38
|
+
function getRepoRoot(workspaceRoot) {
|
|
39
|
+
return workspaceRoot && typeof workspaceRoot === 'string' && workspaceRoot.trim() !== ''
|
|
40
|
+
? path.resolve(workspaceRoot)
|
|
41
|
+
: getWorkspaceRoot();
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
function resolveArchitecturePath(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
43
|
-
return path.join(getRepoRoot(), architecturePath);
|
|
44
|
+
function resolveArchitecturePath(architecturePath = DEFAULT_GRAPH_PATH, workspaceRoot) {
|
|
45
|
+
return path.join(getRepoRoot(workspaceRoot), architecturePath);
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
function resolveSyncStatePath() {
|
|
47
|
-
return path.join(getRepoRoot(), SYNC_STATE_RELATIVE_PATH);
|
|
48
|
+
function resolveSyncStatePath(workspaceRoot) {
|
|
49
|
+
return path.join(getRepoRoot(workspaceRoot), SYNC_STATE_RELATIVE_PATH);
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
function getDefaultNeo4jDatabaseName() {
|
|
51
|
-
const repoName = path.basename(getRepoRoot());
|
|
52
|
+
function getDefaultNeo4jDatabaseName(workspaceRoot) {
|
|
53
|
+
const repoName = path.basename(getRepoRoot(workspaceRoot));
|
|
52
54
|
const normalized = String(repoName)
|
|
53
55
|
.toLowerCase()
|
|
54
56
|
.replace(/[^a-z0-9.-]+/g, '-')
|
|
@@ -86,7 +88,7 @@ function getNeo4jConfig(overrides = {}) {
|
|
|
86
88
|
uri: external.neo4jUri,
|
|
87
89
|
username: external.neo4jUsername,
|
|
88
90
|
password: external.neo4jPassword,
|
|
89
|
-
database: overrides.database || process.env.ARGO_NEO4J_DATABASE || getDefaultNeo4jDatabaseName(),
|
|
91
|
+
database: overrides.database || process.env.ARGO_NEO4J_DATABASE || getDefaultNeo4jDatabaseName(overrides.workspaceRoot),
|
|
90
92
|
};
|
|
91
93
|
}
|
|
92
94
|
|
|
@@ -133,8 +135,8 @@ function createDriver(config = {}) {
|
|
|
133
135
|
);
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
function readArchitectureDocument(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
137
|
-
const absolutePath = resolveArchitecturePath(architecturePath);
|
|
138
|
+
function readArchitectureDocument(architecturePath = DEFAULT_GRAPH_PATH, workspaceRoot) {
|
|
139
|
+
const absolutePath = resolveArchitecturePath(architecturePath, workspaceRoot);
|
|
138
140
|
if (!fs.existsSync(absolutePath)) {
|
|
139
141
|
throw new Error(`System architecture file is missing at ${architecturePath}`);
|
|
140
142
|
}
|
|
@@ -146,8 +148,8 @@ function readArchitectureDocument(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
150
|
|
|
149
|
-
function digestCanonicalArchitecture(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
150
|
-
const absolutePath = resolveArchitecturePath(architecturePath);
|
|
151
|
+
function digestCanonicalArchitecture(architecturePath = DEFAULT_GRAPH_PATH, workspaceRoot) {
|
|
152
|
+
const absolutePath = resolveArchitecturePath(architecturePath, workspaceRoot);
|
|
151
153
|
try {
|
|
152
154
|
return crypto.createHash('sha256').update(fs.readFileSync(absolutePath)).digest('hex');
|
|
153
155
|
} catch {
|
|
@@ -188,8 +190,8 @@ function createEmptySyncState() {
|
|
|
188
190
|
};
|
|
189
191
|
}
|
|
190
192
|
|
|
191
|
-
function readNeo4jSyncState() {
|
|
192
|
-
const statePath = resolveSyncStatePath();
|
|
193
|
+
function readNeo4jSyncState(workspaceRoot) {
|
|
194
|
+
const statePath = resolveSyncStatePath(workspaceRoot);
|
|
193
195
|
if (!fs.existsSync(statePath)) {
|
|
194
196
|
return createEmptySyncState();
|
|
195
197
|
}
|
|
@@ -205,15 +207,15 @@ function readNeo4jSyncState() {
|
|
|
205
207
|
}
|
|
206
208
|
}
|
|
207
209
|
|
|
208
|
-
function writeNeo4jSyncState(state) {
|
|
209
|
-
const statePath = resolveSyncStatePath();
|
|
210
|
+
function writeNeo4jSyncState(state, workspaceRoot) {
|
|
211
|
+
const statePath = resolveSyncStatePath(workspaceRoot);
|
|
210
212
|
fs.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
211
213
|
fs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
|
|
212
214
|
}
|
|
213
215
|
|
|
214
|
-
function getNeo4jGraphSyncState(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
216
|
+
function getNeo4jGraphSyncState(architecturePath = DEFAULT_GRAPH_PATH, workspaceRoot) {
|
|
215
217
|
const graphKey = buildGraphKey(architecturePath);
|
|
216
|
-
const state = readNeo4jSyncState();
|
|
218
|
+
const state = readNeo4jSyncState(workspaceRoot);
|
|
217
219
|
return {
|
|
218
220
|
graphKey,
|
|
219
221
|
dirty: false,
|
|
@@ -221,30 +223,30 @@ function getNeo4jGraphSyncState(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
|
221
223
|
};
|
|
222
224
|
}
|
|
223
225
|
|
|
224
|
-
function updateNeo4jGraphSyncState(architecturePath, patch) {
|
|
226
|
+
function updateNeo4jGraphSyncState(architecturePath, patch, workspaceRoot) {
|
|
225
227
|
const graphKey = buildGraphKey(architecturePath);
|
|
226
|
-
const state = readNeo4jSyncState();
|
|
228
|
+
const state = readNeo4jSyncState(workspaceRoot);
|
|
227
229
|
state.graphs[graphKey] = {
|
|
228
230
|
graphKey,
|
|
229
231
|
dirty: false,
|
|
230
232
|
...(state.graphs[graphKey] || {}),
|
|
231
233
|
...patch,
|
|
232
234
|
};
|
|
233
|
-
writeNeo4jSyncState(state);
|
|
235
|
+
writeNeo4jSyncState(state, workspaceRoot);
|
|
234
236
|
return state.graphs[graphKey];
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
function markNeo4jSyncDirty(architecturePath, error) {
|
|
239
|
+
function markNeo4jSyncDirty(architecturePath, error, workspaceRoot) {
|
|
238
240
|
return updateNeo4jGraphSyncState(architecturePath, {
|
|
239
241
|
dirty: true,
|
|
240
242
|
lastError: String(error && error.message ? error.message : error),
|
|
241
243
|
lastFailureAt: new Date().toISOString(),
|
|
242
|
-
});
|
|
244
|
+
}, workspaceRoot);
|
|
243
245
|
}
|
|
244
246
|
|
|
245
|
-
function markNeo4jSyncClean(architecturePath, verification) {
|
|
246
|
-
const current = getNeo4jGraphSyncState(architecturePath);
|
|
247
|
-
const canonicalDigest = digestCanonicalArchitecture(architecturePath);
|
|
247
|
+
function markNeo4jSyncClean(architecturePath, verification, workspaceRoot) {
|
|
248
|
+
const current = getNeo4jGraphSyncState(architecturePath, workspaceRoot);
|
|
249
|
+
const canonicalDigest = digestCanonicalArchitecture(architecturePath, workspaceRoot);
|
|
248
250
|
return updateNeo4jGraphSyncState(architecturePath, {
|
|
249
251
|
dirty: false,
|
|
250
252
|
lastError: undefined,
|
|
@@ -252,7 +254,7 @@ function markNeo4jSyncClean(architecturePath, verification) {
|
|
|
252
254
|
lastSuccessAt: new Date().toISOString(),
|
|
253
255
|
lastVerifiedCounts: verification ? verification.actual : current.lastVerifiedCounts,
|
|
254
256
|
canonicalDigest,
|
|
255
|
-
});
|
|
257
|
+
}, workspaceRoot);
|
|
256
258
|
}
|
|
257
259
|
|
|
258
260
|
function isCanonicalArchitecturePath(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
@@ -544,8 +546,9 @@ async function writeSubdiagramLinks(tx, graphKey, elements) {
|
|
|
544
546
|
|
|
545
547
|
async function syncArchitectureToNeo4j(options = {}) {
|
|
546
548
|
const architecturePath = options.architecturePath || DEFAULT_GRAPH_PATH;
|
|
549
|
+
const workspaceRoot = options.workspaceRoot;
|
|
547
550
|
const graphKey = buildGraphKey(architecturePath);
|
|
548
|
-
const document = options.document || readArchitectureDocument(architecturePath);
|
|
551
|
+
const document = options.document || readArchitectureDocument(architecturePath, workspaceRoot);
|
|
549
552
|
const config = getNeo4jConfig(options);
|
|
550
553
|
const driver = options.driver || createDriver(config);
|
|
551
554
|
const ownDriver = !options.driver;
|
|
@@ -583,7 +586,7 @@ async function syncArchitectureToNeo4j(options = {}) {
|
|
|
583
586
|
}
|
|
584
587
|
|
|
585
588
|
if (isCanonicalArchitecturePath(architecturePath)) {
|
|
586
|
-
markNeo4jSyncClean(architecturePath, verification);
|
|
589
|
+
markNeo4jSyncClean(architecturePath, verification, workspaceRoot);
|
|
587
590
|
}
|
|
588
591
|
|
|
589
592
|
return {
|
|
@@ -598,7 +601,7 @@ async function syncArchitectureToNeo4j(options = {}) {
|
|
|
598
601
|
};
|
|
599
602
|
} catch (error) {
|
|
600
603
|
if (isCanonicalArchitecturePath(architecturePath)) {
|
|
601
|
-
markNeo4jSyncDirty(architecturePath, error);
|
|
604
|
+
markNeo4jSyncDirty(architecturePath, error, workspaceRoot);
|
|
602
605
|
}
|
|
603
606
|
throw error;
|
|
604
607
|
} finally {
|
|
@@ -610,6 +613,7 @@ async function syncArchitectureToNeo4j(options = {}) {
|
|
|
610
613
|
|
|
611
614
|
async function recoverNeo4jSyncIfNeeded(options = {}) {
|
|
612
615
|
const architecturePath = options.architecturePath || DEFAULT_GRAPH_PATH;
|
|
616
|
+
const workspaceRoot = options.workspaceRoot;
|
|
613
617
|
if (!isCanonicalArchitecturePath(architecturePath)) {
|
|
614
618
|
return {
|
|
615
619
|
attempted: false,
|
|
@@ -618,8 +622,8 @@ async function recoverNeo4jSyncIfNeeded(options = {}) {
|
|
|
618
622
|
};
|
|
619
623
|
}
|
|
620
624
|
|
|
621
|
-
const syncState = getNeo4jGraphSyncState(architecturePath);
|
|
622
|
-
const stale = isNeo4jGraphSyncStale(syncState, digestCanonicalArchitecture(architecturePath));
|
|
625
|
+
const syncState = getNeo4jGraphSyncState(architecturePath, workspaceRoot);
|
|
626
|
+
const stale = isNeo4jGraphSyncStale(syncState, digestCanonicalArchitecture(architecturePath, workspaceRoot));
|
|
623
627
|
if (!syncState.dirty && !stale) {
|
|
624
628
|
return {
|
|
625
629
|
attempted: false,
|
|
@@ -658,8 +662,9 @@ async function recoverNeo4jSyncIfNeeded(options = {}) {
|
|
|
658
662
|
|
|
659
663
|
async function verifyArchitectureSync(options = {}) {
|
|
660
664
|
const architecturePath = options.architecturePath || DEFAULT_GRAPH_PATH;
|
|
665
|
+
const workspaceRoot = options.workspaceRoot;
|
|
661
666
|
const graphKey = buildGraphKey(architecturePath);
|
|
662
|
-
const document = options.document || readArchitectureDocument(architecturePath);
|
|
667
|
+
const document = options.document || readArchitectureDocument(architecturePath, workspaceRoot);
|
|
663
668
|
const config = getNeo4jConfig(options);
|
|
664
669
|
const driver = options.driver || createDriver(config);
|
|
665
670
|
const ownDriver = !options.driver;
|
|
@@ -164,6 +164,7 @@ const {
|
|
|
164
164
|
} = require('./graph-rag/mutationEmbeddingVectorLifecycle.js');
|
|
165
165
|
const {
|
|
166
166
|
DEFAULT_GRAPH_PATH: NEO4J_DEFAULT_GRAPH_PATH,
|
|
167
|
+
assertReadOnlyCypher,
|
|
167
168
|
buildGraphKey,
|
|
168
169
|
buildNeo4jGraphSchema,
|
|
169
170
|
recoverNeo4jSyncIfNeeded,
|
|
@@ -498,6 +499,7 @@ async function syncCanonicalStructuralProjection(request) {
|
|
|
498
499
|
return syncArchitectureToNeo4j({
|
|
499
500
|
architecturePath: context.graphPath.relativePath,
|
|
500
501
|
document: context.document,
|
|
502
|
+
workspaceRoot: context.workspaceRoot,
|
|
501
503
|
});
|
|
502
504
|
}
|
|
503
505
|
|
|
@@ -556,6 +558,7 @@ async function loadContext(args = {}) {
|
|
|
556
558
|
context.neo4jSyncRecovery = await recoverNeo4jSyncIfNeeded({
|
|
557
559
|
architecturePath: graphPath.relativePath,
|
|
558
560
|
document: context.document,
|
|
561
|
+
workspaceRoot: context.workspaceRoot,
|
|
559
562
|
});
|
|
560
563
|
return context;
|
|
561
564
|
}
|
|
@@ -1536,6 +1539,7 @@ async function buildMutationResult(context, mutations, write) {
|
|
|
1536
1539
|
const syncResult = await syncArchitectureToNeo4j({
|
|
1537
1540
|
architecturePath: context.graphPath.relativePath,
|
|
1538
1541
|
document: mutationResult.document,
|
|
1542
|
+
workspaceRoot: context.workspaceRoot,
|
|
1539
1543
|
});
|
|
1540
1544
|
result.neo4jSync = {
|
|
1541
1545
|
status: 'passed',
|
|
@@ -2179,24 +2183,49 @@ async function callTool(name, args = {}, dependencies = undefined) {
|
|
|
2179
2183
|
|
|
2180
2184
|
async function queryNeo4jGraphTool(args = {}) {
|
|
2181
2185
|
const architecturePath = args.architecturePath || DEFAULT_GRAPH_PATH;
|
|
2186
|
+
const workspaceRoot = resolveWorkspaceRoot(args);
|
|
2182
2187
|
|
|
2183
2188
|
if (args.schema === true) {
|
|
2184
|
-
return queryNeo4jGraphSchemaResult(architecturePath);
|
|
2189
|
+
return queryNeo4jGraphSchemaResult(architecturePath, workspaceRoot);
|
|
2185
2190
|
}
|
|
2186
2191
|
|
|
2192
|
+
let recovery = null;
|
|
2187
2193
|
try {
|
|
2194
|
+
// Reject invalid/write queries before any side effect.
|
|
2195
|
+
assertReadOnlyCypher(args.cypher);
|
|
2196
|
+
|
|
2197
|
+
// Same projection-recovery mechanism as the loadContext-based tools
|
|
2198
|
+
// (e.g. getSystemArchitecture): when the sync state is dirty or the
|
|
2199
|
+
// recorded canonicalDigest no longer matches the canonical JSON, rebuild
|
|
2200
|
+
// the Neo4j projection before servicing the read-only query.
|
|
2201
|
+
recovery = await recoverNeo4jSyncIfNeeded({
|
|
2202
|
+
architecturePath,
|
|
2203
|
+
workspaceRoot,
|
|
2204
|
+
});
|
|
2205
|
+
|
|
2188
2206
|
const result = await runNeo4jCypherQuery({
|
|
2189
2207
|
architecturePath,
|
|
2190
2208
|
cypher: args.cypher,
|
|
2209
|
+
workspaceRoot,
|
|
2191
2210
|
});
|
|
2192
|
-
|
|
2211
|
+
const payload = {
|
|
2193
2212
|
status: 'passed',
|
|
2194
2213
|
architecturePath: result.architecturePath,
|
|
2195
2214
|
graphKey: result.graphKey,
|
|
2196
2215
|
database: result.database,
|
|
2197
2216
|
records: result.records,
|
|
2198
2217
|
summary: result.summary,
|
|
2199
|
-
}
|
|
2218
|
+
};
|
|
2219
|
+
if (recovery.attempted) {
|
|
2220
|
+
payload.neo4jRecovery = recovery;
|
|
2221
|
+
if (recovery.status === 'failed') {
|
|
2222
|
+
payload.warnings = [
|
|
2223
|
+
`Neo4j automatic resync failed before servicing ${architecturePath}: ${recovery.error}`,
|
|
2224
|
+
'The read-only query still ran against the current projection. Neo4j will be retried on the next canonical read or write, or you can run node .argo/scripts/syncSystemArchitectureToNeo4j.js manually.',
|
|
2225
|
+
];
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
return toolResult(payload);
|
|
2200
2229
|
} catch (error) {
|
|
2201
2230
|
return toolResult(queryError(
|
|
2202
2231
|
error && error.category ? error.category : 'NEO4J_QUERY_FAILED',
|
|
@@ -2207,17 +2236,18 @@ async function queryNeo4jGraphTool(args = {}) {
|
|
|
2207
2236
|
...(error && error.clause ? { clause: error.clause } : {}),
|
|
2208
2237
|
...(error && error.queriedDatabase ? { queriedDatabase: error.queriedDatabase } : {}),
|
|
2209
2238
|
...(error && error.expectedDatabase ? { expectedDatabase: error.expectedDatabase } : {}),
|
|
2239
|
+
...(recovery && recovery.attempted ? { neo4jRecovery: recovery } : {}),
|
|
2210
2240
|
},
|
|
2211
2241
|
));
|
|
2212
2242
|
}
|
|
2213
2243
|
}
|
|
2214
2244
|
|
|
2215
|
-
function queryNeo4jGraphSchemaResult(architecturePath) {
|
|
2245
|
+
function queryNeo4jGraphSchemaResult(architecturePath, workspaceRoot) {
|
|
2216
2246
|
const schema = buildNeo4jGraphSchema(architecturePath);
|
|
2217
2247
|
let typeEnums = {};
|
|
2218
2248
|
try {
|
|
2219
|
-
const
|
|
2220
|
-
const schemaPath = resolveSchemaPath(
|
|
2249
|
+
const resolvedRoot = workspaceRoot || resolveWorkspaceRoot({ architecturePath });
|
|
2250
|
+
const schemaPath = resolveSchemaPath(resolvedRoot);
|
|
2221
2251
|
const jsonSchema = readJson(schemaPath.absolutePath, schemaPath.relativePath);
|
|
2222
2252
|
typeEnums = {
|
|
2223
2253
|
archimateElementTypes: (jsonSchema.$defs.archimateElementType || {}).enum || [],
|
package/install-argo.ps1
CHANGED
|
@@ -726,7 +726,7 @@ if ($SkipDsh) {
|
|
|
726
726
|
Write-DshManagedBlock -Path $patchPath -Block $block -MarkerStart '# BEGIN ArchGraph ARGO deployment' -MarkerEnd '# END ArchGraph ARGO deployment'
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
-
Write-Host "[19/19] argo\
|
|
729
|
+
Write-Host "[19/19] argo\agents11 -> $DshHome\.agent-presets\<id> (DeepSeek Harness agent presets)"
|
|
730
730
|
New-DshAgentPresets -DshHome $DshHome -AgentsSrc (Join-Path $argoDir 'agents')
|
|
731
731
|
|
|
732
732
|
Write-Host ' Restart `dsh web` to activate the MCP bridge and the wakeup plugin;'
|
package/package.json
CHANGED