hoi4-agent-tools 2.3.3 → 2.3.5
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 +10 -0
- package/dist/hoi4_agent_tools/core/domain-scan-patterns.js +66 -0
- package/dist/hoi4_agent_tools/event/graph.js +14 -0
- package/dist/hoi4_agent_tools/event/service.js +30 -13
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +9 -1
- package/dist/hoi4_agent_tools/mcp/tools/chaosx.js +3 -1
- package/dist/hoi4_agent_tools/mcp/tools/event.js +10 -3
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +5 -4
- package/dist/hoi4_agent_tools/mcp/tools/map.js +5 -7
- package/dist/hoi4_agent_tools/probability/service.js +8 -4
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/events.md +2 -2
- package/package.json +1 -1
- package/schemas/configuration.schema.json +1 -1
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/map-operation.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +1 -1
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.5 - 2026-08-02
|
|
4
|
+
|
|
5
|
+
- Replaced all-domain scans in focus, map, probability, rewrite validation, and country-asset discovery with domain-specific source inventories.
|
|
6
|
+
- Prevented large unrelated mod files from producing `SCAN_BYTE_LIMIT` errors in otherwise bounded MCP work and added low-ceiling regression coverage.
|
|
7
|
+
|
|
8
|
+
## 2.3.4 - 2026-07-28
|
|
9
|
+
|
|
10
|
+
- Bounded large game-backed event scans before workspace-wide lifecycle and helper analysis could exhaust the MCP process and close the stdio transport.
|
|
11
|
+
- Added automatic focused event analysis for large workspaces, explicit deferred-analysis diagnostics, source-profile reporting, and deterministic regression coverage.
|
|
12
|
+
|
|
3
13
|
## 2.3.3 - 2026-07-26
|
|
4
14
|
|
|
5
15
|
- Regenerated the npm lockfile with the pinned release npm so clean installs include the patched optional native runtime packages.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { compareCodeUnits } from './canonical.js';
|
|
2
|
+
function under(roots, glob) {
|
|
3
|
+
return roots.map((root) => `${root.replaceAll('\\', '/').replace(/\/$/u, '')}/${glob}`);
|
|
4
|
+
}
|
|
5
|
+
function uniquePatterns(patterns) {
|
|
6
|
+
return [...new Set(patterns)].sort(compareCodeUnits);
|
|
7
|
+
}
|
|
8
|
+
function englishLocalisationPatterns(roots) {
|
|
9
|
+
return [...under(roots, 'english/**/*.{yml,yaml}'), ...under(roots, '*.{yml,yaml}')];
|
|
10
|
+
}
|
|
11
|
+
/** Sources required to import, link, lint, render, and rewrite focus content. */
|
|
12
|
+
export function focusDomainScanPatterns(workspace) {
|
|
13
|
+
const roots = workspace.registration.roots;
|
|
14
|
+
return uniquePatterns([
|
|
15
|
+
...under(roots.focus, '**/*.txt'),
|
|
16
|
+
...under(roots.focus, '**/*.focus-plan.json'),
|
|
17
|
+
'common/continuous_focus/**/*.txt',
|
|
18
|
+
'common/decisions/**/*.txt',
|
|
19
|
+
'common/ideas/**/*.txt',
|
|
20
|
+
'common/characters/**/*.txt',
|
|
21
|
+
'common/scripted_effects/**/*.txt',
|
|
22
|
+
'common/scripted_triggers/**/*.txt',
|
|
23
|
+
'common/script_constants/**/*.txt',
|
|
24
|
+
'common/mtth/**/*.txt',
|
|
25
|
+
'events/**/*.txt',
|
|
26
|
+
...under(roots.interface, '**/*.gfx'),
|
|
27
|
+
...under(roots.gfx, '**/*.gfx'),
|
|
28
|
+
...englishLocalisationPatterns(roots.localisation),
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
/** Weighted source surfaces and shared definitions used by probability analysis. */
|
|
32
|
+
export function probabilityDomainScanPatterns(workspace, sourcePaths = []) {
|
|
33
|
+
const roots = workspace.registration.roots;
|
|
34
|
+
const normalizedSourcePaths = sourcePaths.map((sourcePath) => sourcePath.replace(/^.*?:/u, '').replaceAll('\\', '/').replace(/^\.\//u, ''));
|
|
35
|
+
return uniquePatterns([
|
|
36
|
+
...normalizedSourcePaths,
|
|
37
|
+
...under(roots.focus, '**/*.txt'),
|
|
38
|
+
'common/decisions/**/*.txt',
|
|
39
|
+
'common/technologies/**/*.txt',
|
|
40
|
+
'common/doctrines/**/*.txt',
|
|
41
|
+
'common/ai_strategy/**/*.txt',
|
|
42
|
+
'common/ai_strategy_plans/**/*.txt',
|
|
43
|
+
'common/ai_focuses/**/*.txt',
|
|
44
|
+
'common/on_actions/**/*.txt',
|
|
45
|
+
'common/scripted_effects/**/*.txt',
|
|
46
|
+
'common/scripted_triggers/**/*.txt',
|
|
47
|
+
'common/script_constants/**/*.txt',
|
|
48
|
+
'common/mtth/**/*.txt',
|
|
49
|
+
'events/**/*.txt',
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
/** Text sources needed to discover country flags and leader portraits. */
|
|
53
|
+
export function countryAssetDiscoveryPatterns(workspace) {
|
|
54
|
+
const roots = workspace.registration.roots;
|
|
55
|
+
return uniquePatterns([
|
|
56
|
+
'history/countries/**/*.txt',
|
|
57
|
+
'common/characters/**/*.txt',
|
|
58
|
+
'common/scripted_effects/**/*.txt',
|
|
59
|
+
'events/**/*.txt',
|
|
60
|
+
...under(roots.interface, '**/*.gfx'),
|
|
61
|
+
...under(roots.gfx, '**/*.gfx'),
|
|
62
|
+
]);
|
|
63
|
+
}
|
|
64
|
+
export function exactChangedFilePatterns(relativePaths) {
|
|
65
|
+
return uniquePatterns(relativePaths.map((relativePath) => relativePath.replaceAll('\\', '/').replace(/^\.\//u, '')));
|
|
66
|
+
}
|
|
@@ -1571,6 +1571,20 @@ export function buildEventGraph(snapshot, options = {}) {
|
|
|
1571
1571
|
graph.stateAccesses = stableUnique(graph.stateAccesses);
|
|
1572
1572
|
graph.unresolved = stableUnique(graph.unresolved);
|
|
1573
1573
|
const focused = options.analysisMode === 'focused';
|
|
1574
|
+
if (focused) {
|
|
1575
|
+
graph.unresolved.push({
|
|
1576
|
+
id: 'event-analysis-focused-boundary',
|
|
1577
|
+
kind: 'partial_source',
|
|
1578
|
+
expression: 'workspace-wide event helper projections and lifecycle diagnostics',
|
|
1579
|
+
confidence: 'unresolved',
|
|
1580
|
+
blockers: [
|
|
1581
|
+
{
|
|
1582
|
+
code: 'EVENT_FOCUSED_ANALYSIS_DEFERRED',
|
|
1583
|
+
message: 'Large event workspaces defer workspace-wide helper projections and lifecycle passes; direct source evidence remains available.',
|
|
1584
|
+
},
|
|
1585
|
+
],
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1574
1588
|
const stateLinks = focused ? [] : stateLinksFor(graph, budget);
|
|
1575
1589
|
if (!focused) {
|
|
1576
1590
|
duplicateAndReferenceIssues(graph);
|
|
@@ -45,6 +45,9 @@ class BoundedEventFragmentCache {
|
|
|
45
45
|
function eventGraphCacheKey(workspaceId, projectHelpers, analysisMode = 'full') {
|
|
46
46
|
return `${workspaceId}\0${projectHelpers ? 'expanded' : 'structural'}\0${analysisMode}`;
|
|
47
47
|
}
|
|
48
|
+
function isGameBackedWorkspace(workspace) {
|
|
49
|
+
return workspace.roots.some(({ kind }) => kind === 'game');
|
|
50
|
+
}
|
|
48
51
|
const serviceState = new WeakMap();
|
|
49
52
|
const EVENT_GRAPH_ARTIFACT_MAX_BYTES = 67_108_864;
|
|
50
53
|
const EVENT_GRAPH_ARTIFACT_MAX_CHUNKS = 1_024;
|
|
@@ -77,6 +80,7 @@ export function eventScanReport(graph, fullGraphRecordLimit = EVENT_SCAN_FULL_GR
|
|
|
77
80
|
graphSummary: {
|
|
78
81
|
schemaVersion: graph.schemaVersion,
|
|
79
82
|
parserVersion: graph.parserVersion,
|
|
83
|
+
analysisMode: graph.analysisMode ?? 'full',
|
|
80
84
|
workspaceId: graph.workspaceId,
|
|
81
85
|
workspaceIdentity: graph.workspaceIdentity,
|
|
82
86
|
revision: graph.revision,
|
|
@@ -122,6 +126,7 @@ function cachedEventGraphHash(graph, signal) {
|
|
|
122
126
|
? hashCanonical({
|
|
123
127
|
schemaVersion: graph.schemaVersion,
|
|
124
128
|
parserVersion: graph.parserVersion,
|
|
129
|
+
analysisMode: graph.analysisMode ?? 'full',
|
|
125
130
|
workspaceIdentity: graph.workspaceIdentity,
|
|
126
131
|
revision: graph.revision,
|
|
127
132
|
statistics: graph.statistics,
|
|
@@ -146,9 +151,12 @@ function sharedState(engine) {
|
|
|
146
151
|
function engineGeneration(engine, workspaceId) {
|
|
147
152
|
return engine.generation(workspaceId);
|
|
148
153
|
}
|
|
149
|
-
function eventScanPatterns(workspace) {
|
|
154
|
+
function eventScanPatterns(workspace, focused = false) {
|
|
150
155
|
const roots = workspace.registration.roots;
|
|
151
156
|
const under = (values, glob) => values.map((root) => `${root.replaceAll('\\', '/').replace(/\/$/u, '')}/${glob}`);
|
|
157
|
+
if (focused) {
|
|
158
|
+
return ['events/**/*.txt', 'common/on_actions/**/*.txt'].sort(compareCodeUnits);
|
|
159
|
+
}
|
|
152
160
|
return [
|
|
153
161
|
'events/**/*.txt',
|
|
154
162
|
'history/countries/**/*.txt',
|
|
@@ -171,13 +179,16 @@ function eventScanPatterns(workspace) {
|
|
|
171
179
|
...under(roots.localisation, '*.{yml,yaml}'),
|
|
172
180
|
].sort(compareCodeUnits);
|
|
173
181
|
}
|
|
174
|
-
async function scanEventSources(engine, workspaceId, workspace, principal, signal) {
|
|
175
|
-
const patterns = eventScanPatterns(workspace);
|
|
182
|
+
async function scanEventSources(engine, workspaceId, workspace, focused = false, principal, signal) {
|
|
183
|
+
const patterns = eventScanPatterns(workspace, focused);
|
|
184
|
+
const gamePatterns = eventScanPatterns(workspace, focused);
|
|
176
185
|
const sourceKinds = ['mod', 'dependency', 'fixture'];
|
|
177
186
|
const scans = await Promise.all([
|
|
178
187
|
engine.scan(workspaceId, { patterns, rootKinds: sourceKinds }, principal, signal),
|
|
179
188
|
...(workspace.roots.some(({ kind }) => kind === 'game')
|
|
180
|
-
? [
|
|
189
|
+
? [
|
|
190
|
+
engine.scan(workspaceId, { patterns: gamePatterns, rootKinds: ['game'] }, principal, signal),
|
|
191
|
+
]
|
|
181
192
|
: []),
|
|
182
193
|
]);
|
|
183
194
|
const files = scans.flatMap(({ files }) => files.map((file) => ({ ...file })));
|
|
@@ -691,26 +702,32 @@ export class EventChainViewer {
|
|
|
691
702
|
// principal bypass the resolver check for a later caller.
|
|
692
703
|
const workspace = this.engine.resolver.get(workspaceId, options.principal);
|
|
693
704
|
const generation = engineGeneration(this.engine, workspaceId);
|
|
694
|
-
const
|
|
695
|
-
const
|
|
696
|
-
const
|
|
705
|
+
const requestedProjectHelpers = options.projectHelpers ?? true;
|
|
706
|
+
const requestedAnalysisMode = options.analysisMode ?? 'full';
|
|
707
|
+
const autoFocused = isGameBackedWorkspace(workspace) && requestedAnalysisMode === 'full';
|
|
708
|
+
const cacheAnalysisMode = autoFocused ? 'focused' : requestedAnalysisMode;
|
|
709
|
+
const cacheProjectHelpers = cacheAnalysisMode === 'focused' ? false : requestedProjectHelpers;
|
|
710
|
+
const cacheKey = eventGraphCacheKey(workspaceId, cacheProjectHelpers, cacheAnalysisMode);
|
|
697
711
|
const cached = this.#state.current.get(cacheKey);
|
|
698
712
|
if (options.refresh !== true && cached?.generation === generation) {
|
|
699
713
|
return cached.graph;
|
|
700
714
|
}
|
|
701
|
-
const sibling = this.#state.current.get(eventGraphCacheKey(workspaceId, !
|
|
702
|
-
this.#state.current.get(eventGraphCacheKey(workspaceId,
|
|
703
|
-
this.#state.current.get(eventGraphCacheKey(workspaceId,
|
|
715
|
+
const sibling = this.#state.current.get(eventGraphCacheKey(workspaceId, !cacheProjectHelpers, cacheAnalysisMode)) ??
|
|
716
|
+
this.#state.current.get(eventGraphCacheKey(workspaceId, requestedProjectHelpers, requestedAnalysisMode)) ??
|
|
717
|
+
this.#state.current.get(eventGraphCacheKey(workspaceId, !requestedProjectHelpers)) ??
|
|
718
|
+
this.#state.current.get(eventGraphCacheKey(workspaceId, requestedProjectHelpers));
|
|
719
|
+
const scanFocused = autoFocused || requestedAnalysisMode === 'focused';
|
|
704
720
|
const snapshot = options.refresh !== true && sibling?.generation === generation
|
|
705
721
|
? sibling.snapshot
|
|
706
|
-
: await scanEventSources(this.engine, workspaceId, workspace, options.principal, options.signal);
|
|
722
|
+
: await scanEventSources(this.engine, workspaceId, workspace, scanFocused, options.principal, options.signal);
|
|
707
723
|
options.signal?.throwIfAborted();
|
|
708
724
|
if (cached?.snapshot.revision === snapshot.revision && cached.generation === generation) {
|
|
709
725
|
return cached.graph;
|
|
710
726
|
}
|
|
711
|
-
const buildAnalysisMode =
|
|
727
|
+
const buildAnalysisMode = autoFocused || snapshot.files.length > EVENT_FOCUSED_GRAPH_FILE_THRESHOLD
|
|
712
728
|
? 'focused'
|
|
713
729
|
: 'full';
|
|
730
|
+
const projectHelpers = buildAnalysisMode === 'focused' ? false : requestedProjectHelpers;
|
|
714
731
|
const graph = buildEventGraph(snapshot, {
|
|
715
732
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
716
733
|
cache: this.#state.fragments,
|
|
@@ -719,7 +736,7 @@ export class EventChainViewer {
|
|
|
719
736
|
analysisMode: buildAnalysisMode,
|
|
720
737
|
});
|
|
721
738
|
this.#state.current.set(cacheKey, { generation, snapshot, graph });
|
|
722
|
-
if (
|
|
739
|
+
if (buildAnalysisMode === 'full')
|
|
723
740
|
rememberGraph(this.#state, workspaceId, graph);
|
|
724
741
|
return graph;
|
|
725
742
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { boundedSourceHashEvidence, publicArtifactLink } from '../../core/artifacts.js';
|
|
2
2
|
import { canonicalJson } from '../../core/canonical.js';
|
|
3
|
+
import { exactChangedFilePatterns, focusDomainScanPatterns, } from '../../core/domain-scan-patterns.js';
|
|
3
4
|
import { ServiceError } from '../../core/result.js';
|
|
4
5
|
import { parseClausewitz } from '../../core/source/index.js';
|
|
5
6
|
import { TRANSACTION_MAX_DIAGNOSTICS } from '../../core/transaction-limits.js';
|
|
@@ -35,7 +36,14 @@ function focusReferences(engineSnapshot) {
|
|
|
35
36
|
}
|
|
36
37
|
export async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
37
38
|
engine.invalidate(transaction.workspaceId);
|
|
38
|
-
const
|
|
39
|
+
const workspace = engine.resolver.get(transaction.workspaceId, principal);
|
|
40
|
+
const focusTransaction = transaction.operationKind === 'focus-plan-changes' ||
|
|
41
|
+
transaction.operationKind === 'continuous-focus-plan-changes';
|
|
42
|
+
const snapshot = await engine.scan(transaction.workspaceId, {
|
|
43
|
+
patterns: focusTransaction
|
|
44
|
+
? focusDomainScanPatterns(workspace)
|
|
45
|
+
: exactChangedFilePatterns(transaction.files.map(({ relativePath }) => relativePath)),
|
|
46
|
+
}, principal, signal);
|
|
39
47
|
const changed = new Set(transaction.files.map(({ relativePath }) => sourcePathComparisonKey(relativePath)));
|
|
40
48
|
const diagnostics = snapshot.diagnostics.filter(({ location }) => {
|
|
41
49
|
if (location === undefined)
|
|
@@ -3,6 +3,7 @@ import sharp from 'sharp';
|
|
|
3
3
|
import { z } from 'zod/v4';
|
|
4
4
|
import { publicArtifactLink } from '../../core/artifacts.js';
|
|
5
5
|
import { compareCodeUnits, hashCanonical } from '../../core/canonical.js';
|
|
6
|
+
import { countryAssetDiscoveryPatterns } from '../../core/domain-scan-patterns.js';
|
|
6
7
|
import { emptyServiceResult } from '../../core/result.js';
|
|
7
8
|
import { RenderBudget } from '../../core/render-budget.js';
|
|
8
9
|
import { workspaceIdSchema } from '../../schemas/common.js';
|
|
@@ -382,7 +383,8 @@ export function registerChaosxTools(server, engine, context) {
|
|
|
382
383
|
try {
|
|
383
384
|
const progress = progressReporter(extra);
|
|
384
385
|
await progress.report(0, 3, 'Resolving ChaosX country assets');
|
|
385
|
-
const
|
|
386
|
+
const discoveryWorkspace = engine.resolver.get(workspaceId, context.principal);
|
|
387
|
+
const snapshot = await engine.scan(workspaceId, { patterns: countryAssetDiscoveryPatterns(discoveryWorkspace) }, context.principal, progress.signal);
|
|
386
388
|
const tags = [...new Set(input.countryTags)];
|
|
387
389
|
const { selections, catalog } = await selectCountryAssets(engine, snapshot, workspaceId, tags, input.eventId, context, progress.signal);
|
|
388
390
|
await progress.report(1, 3, 'Rasterizing ChaosX country assets');
|
|
@@ -122,6 +122,7 @@ const renderHashSchema = z
|
|
|
122
122
|
const eventInspectOutputSchema = strictOperationResultSchema(z
|
|
123
123
|
.object({
|
|
124
124
|
mode: eventInspectModeSchema,
|
|
125
|
+
analysisMode: z.enum(['full', 'focused']),
|
|
125
126
|
revision: sha256Schema,
|
|
126
127
|
graphHash: sha256Schema,
|
|
127
128
|
counts: eventGraphCountsSchema,
|
|
@@ -131,6 +132,7 @@ const eventInspectOutputSchema = strictOperationResultSchema(z
|
|
|
131
132
|
const eventRenderOutputSchema = strictOperationResultSchema(z
|
|
132
133
|
.object({
|
|
133
134
|
view: eventRenderViewSchema,
|
|
135
|
+
analysisMode: z.enum(['full', 'focused']),
|
|
134
136
|
revision: sha256Schema,
|
|
135
137
|
graphHash: sha256Schema,
|
|
136
138
|
layoutHash: sha256Schema,
|
|
@@ -223,15 +225,18 @@ function graphCounts(graph, artifacts) {
|
|
|
223
225
|
}
|
|
224
226
|
function graphValidation(graph) {
|
|
225
227
|
const counts = graphCounts(graph, 0);
|
|
228
|
+
const focused = graph.analysisMode === 'focused';
|
|
226
229
|
return {
|
|
227
230
|
passed: graph.complete && counts.blockingDiagnostics === 0,
|
|
228
231
|
checks: [
|
|
229
232
|
{
|
|
230
233
|
id: 'event-analysis',
|
|
231
234
|
passed: graph.complete && counts.blockingDiagnostics === 0,
|
|
232
|
-
message:
|
|
233
|
-
?
|
|
234
|
-
:
|
|
235
|
+
message: focused
|
|
236
|
+
? 'Large workspace analysis deferred workspace-wide helper projections and lifecycle passes; direct evidence is linked'
|
|
237
|
+
: graph.complete
|
|
238
|
+
? `${counts.blockingDiagnostics} blocking event-chain diagnostics; full evidence is linked`
|
|
239
|
+
: `${graph.skippedSourceCount} event-analysis source(s) were skipped; full evidence is linked`,
|
|
235
240
|
},
|
|
236
241
|
],
|
|
237
242
|
};
|
|
@@ -314,6 +319,7 @@ export function registerEventTools(server, engine, context) {
|
|
|
314
319
|
await progress.report(2, 3, 'Linking complete event analysis');
|
|
315
320
|
const result = emptyServiceResult(workspaceId, {
|
|
316
321
|
mode: inspected.mode,
|
|
322
|
+
analysisMode: inspected.graph.analysisMode ?? 'full',
|
|
317
323
|
revision: inspected.graph.revision,
|
|
318
324
|
graphHash: inspected.graphHash,
|
|
319
325
|
counts: graphCounts(inspected.graph, inspected.artifacts.length),
|
|
@@ -352,6 +358,7 @@ export function registerEventTools(server, engine, context) {
|
|
|
352
358
|
await progress.report(2, 3, 'Linking complete event renders');
|
|
353
359
|
const result = emptyServiceResult(workspaceId, {
|
|
354
360
|
view: rendered.render.view,
|
|
361
|
+
analysisMode: rendered.graph.analysisMode ?? 'full',
|
|
355
362
|
revision: rendered.graph.revision,
|
|
356
363
|
graphHash: rendered.graphHash,
|
|
357
364
|
layoutHash: rendered.render.layout.layoutHash,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
2
|
import { boundedSourceHashEvidence, publicArtifactLink } from '../../core/artifacts.js';
|
|
3
3
|
import { compareCodeUnits, canonicalJson, hashCanonical } from '../../core/canonical.js';
|
|
4
|
+
import { focusDomainScanPatterns } from '../../core/domain-scan-patterns.js';
|
|
4
5
|
import { RenderBudget } from '../../core/render-budget.js';
|
|
5
6
|
import { emptyServiceResult, ServiceError } from '../../core/result.js';
|
|
6
7
|
import { parseClausewitz } from '../../core/source/index.js';
|
|
@@ -351,9 +352,9 @@ async function executeFocusVisualTool(engine, workbench, context, input, extra,
|
|
|
351
352
|
try {
|
|
352
353
|
const progress = progressReporter(extra);
|
|
353
354
|
await progress.report(0, 4, 'Importing and indexing focus source');
|
|
354
|
-
const snapshot = await engine.scan(workspaceId, {}, context.principal, progress.signal);
|
|
355
|
-
await progress.report(1, 4, 'Focus source index complete');
|
|
356
355
|
const workspace = engine.resolver.get(workspaceId, context.principal);
|
|
356
|
+
const snapshot = await engine.scan(workspaceId, { patterns: focusDomainScanPatterns(workspace) }, context.principal, progress.signal);
|
|
357
|
+
await progress.report(1, 4, 'Focus source index complete');
|
|
357
358
|
const renderBudget = new RenderBudget();
|
|
358
359
|
if (input.mode === 'continuous') {
|
|
359
360
|
selectedContinuousFocusFile(snapshot, relativePath);
|
|
@@ -610,8 +611,8 @@ export function registerFocusTools(server, engine, context) {
|
|
|
610
611
|
try {
|
|
611
612
|
const progress = progressReporter(extra);
|
|
612
613
|
await progress.report(0, 3, 'Building shared workspace index');
|
|
613
|
-
const snapshot = await engine.scan(workspaceId, {}, context.principal, progress.signal);
|
|
614
614
|
const workspace = engine.resolver.get(workspaceId, context.principal);
|
|
615
|
+
const snapshot = await engine.scan(workspaceId, { patterns: focusDomainScanPatterns(workspace) }, context.principal, progress.signal);
|
|
615
616
|
if (input.mode === 'continuous') {
|
|
616
617
|
const selectedFiles = activeContinuousFocusFiles(snapshot).filter((file) => relativePath === undefined || file.relativePath === relativePath);
|
|
617
618
|
if (selectedFiles.length === 0) {
|
|
@@ -849,8 +850,8 @@ export function registerFocusTools(server, engine, context) {
|
|
|
849
850
|
requireServerScope(context, 'hoi4:write');
|
|
850
851
|
const progress = progressReporter(extra);
|
|
851
852
|
await progress.report(0, 5, 'Validating shared index and current source');
|
|
852
|
-
const snapshot = await engine.scan(workspaceId, {}, context.principal, progress.signal);
|
|
853
853
|
const workspace = engine.resolver.get(workspaceId, context.principal);
|
|
854
|
+
const snapshot = await engine.scan(workspaceId, { patterns: focusDomainScanPatterns(workspace) }, context.principal, progress.signal);
|
|
854
855
|
if (input.mode === 'continuous') {
|
|
855
856
|
const plan = input.plan;
|
|
856
857
|
if (createIfMissing &&
|
|
@@ -369,10 +369,8 @@ export function registerMapTools(server, engine, context) {
|
|
|
369
369
|
try {
|
|
370
370
|
const progress = progressReporter(extra);
|
|
371
371
|
await progress.report(0, 3, 'Inspecting and validating the map');
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
nudger.validate(workspaceId, context.principal, progress.signal),
|
|
375
|
-
]);
|
|
372
|
+
const { snapshot, validation } = await nudger.validate(workspaceId, context.principal, progress.signal);
|
|
373
|
+
const sharedRevision = snapshot.revision;
|
|
376
374
|
const selected = selectedMapEntities(snapshot, provinceIds, stateIds, regionIds);
|
|
377
375
|
const provinceGeometry = provinceIds.length === 0
|
|
378
376
|
? undefined
|
|
@@ -392,7 +390,7 @@ export function registerMapTools(server, engine, context) {
|
|
|
392
390
|
schemaVersion: 'map-inspect.v1',
|
|
393
391
|
sourceHashes: sourceEvidence.bounded.sourceHashes,
|
|
394
392
|
metadata: {
|
|
395
|
-
sharedRevision
|
|
393
|
+
sharedRevision,
|
|
396
394
|
sourceHashInventory: sourceEvidence.bounded.inventory,
|
|
397
395
|
},
|
|
398
396
|
};
|
|
@@ -403,7 +401,7 @@ export function registerMapTools(server, engine, context) {
|
|
|
403
401
|
content: `${canonicalJson({
|
|
404
402
|
schemaVersion: 1,
|
|
405
403
|
revision: snapshot.revision,
|
|
406
|
-
sharedRevision
|
|
404
|
+
sharedRevision,
|
|
407
405
|
dimensions: snapshot.index.raster === undefined
|
|
408
406
|
? null
|
|
409
407
|
: {
|
|
@@ -471,7 +469,7 @@ export function registerMapTools(server, engine, context) {
|
|
|
471
469
|
const artifacts = await engine.artifacts.withAtomicChunkedWrites(workspace, artifactWrites, (stored) => Promise.resolve([...stored]), progress.signal);
|
|
472
470
|
const result = emptyServiceResult(workspaceId, {
|
|
473
471
|
revision: snapshot.revision,
|
|
474
|
-
sharedRevision
|
|
472
|
+
sharedRevision,
|
|
475
473
|
width: snapshot.index.raster?.width ?? null,
|
|
476
474
|
height: snapshot.index.raster?.height ?? null,
|
|
477
475
|
definitions: snapshot.index.definitions.length,
|
|
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { canonicalJson, hashCanonical } from '../core/canonical.js';
|
|
4
4
|
import { ClausewitzEvaluationDefinitions } from '../core/clausewitz-evaluation.js';
|
|
5
|
+
import { probabilityDomainScanPatterns } from '../core/domain-scan-patterns.js';
|
|
5
6
|
import { publicArtifactLink } from '../core/artifacts.js';
|
|
6
7
|
import { ServiceError } from '../core/result.js';
|
|
7
8
|
import { PACKAGE_VERSION } from '../version.js';
|
|
@@ -590,10 +591,13 @@ export class ProbabilityAnalyzer {
|
|
|
590
591
|
this.engine = engine;
|
|
591
592
|
this.state = analyzerState(engine);
|
|
592
593
|
}
|
|
593
|
-
async scan(context) {
|
|
594
|
+
async scan(context, sourcePaths = []) {
|
|
594
595
|
if (context.refresh === true)
|
|
595
596
|
this.engine.invalidate(context.workspaceId);
|
|
596
|
-
|
|
597
|
+
const workspace = this.engine.resolver.get(context.workspaceId, context.principal);
|
|
598
|
+
return this.engine.scan(context.workspaceId, {
|
|
599
|
+
patterns: probabilityDomainScanPatterns(workspace, sourcePaths.filter((sourcePath) => sourcePath !== undefined)),
|
|
600
|
+
}, context.principal, context.signal);
|
|
597
601
|
}
|
|
598
602
|
async verifyGameVersion(context, adapter) {
|
|
599
603
|
const workspace = this.engine.resolver.get(context.workspaceId, context.principal);
|
|
@@ -774,7 +778,7 @@ export class ProbabilityAnalyzer {
|
|
|
774
778
|
if (adapter === undefined || source === undefined)
|
|
775
779
|
return { adapters: probabilityAdapters(), artifacts: [], filesScanned: [] };
|
|
776
780
|
const gameVersionVerification = await this.verifyGameVersion(context, adapter);
|
|
777
|
-
const snapshot = await this.scan(context);
|
|
781
|
+
const snapshot = await this.scan(context, [source.path]);
|
|
778
782
|
const surface = discoverWeightedSurface(snapshot, adapter, source, candidatePool);
|
|
779
783
|
const definitions = ClausewitzEvaluationDefinitions.build(snapshot);
|
|
780
784
|
const inspectedCandidates = evaluateExactCandidates(surface, { id: 'inspection', state: {} }, definitions);
|
|
@@ -838,7 +842,7 @@ export class ProbabilityAnalyzer {
|
|
|
838
842
|
request.scenarioSet.workspaceId !== request.workspaceId)
|
|
839
843
|
throw new ServiceError('PROBABILITY_SCENARIO_WORKSPACE_MISMATCH', 'Scenario set belongs to a different workspace');
|
|
840
844
|
const gameVersionVerification = await this.verifyGameVersion(request, request.adapter);
|
|
841
|
-
const snapshot = await this.scan(request);
|
|
845
|
+
const snapshot = await this.scan(request, [request.source.path]);
|
|
842
846
|
const surface = discoverWeightedSurface(snapshot, request.adapter, request.source, request.candidatePool ?? []);
|
|
843
847
|
const definitions = ClausewitzEvaluationDefinitions.build(snapshot);
|
|
844
848
|
const metadata = this.metadata(request, snapshot, surface, request.scenarioSet, request.candidatePool ?? [], gameVersionVerification, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
export const PACKAGE_NAME = 'hoi4-agent-tools';
|
|
3
|
-
export const PACKAGE_VERSION = '2.3.
|
|
3
|
+
export const PACKAGE_VERSION = '2.3.5';
|
|
4
4
|
export const MCP_PROTOCOL_VERSION = LATEST_PROTOCOL_VERSION;
|
|
5
5
|
export const CONFIG_VERSION = 1;
|
|
6
6
|
export const TRANSACTION_VERSION = 1;
|
package/docs/events.md
CHANGED
|
@@ -16,9 +16,9 @@ Use the three event tools directly from the target mod.
|
|
|
16
16
|
- `lint`: find missing targets, unreachable events, suspicious cycles, conflicting definitions, invalid timing, and unresolved dynamic calls.
|
|
17
17
|
- `impact`: identify callers, descendants, state dependencies, and files affected by a proposed event change.
|
|
18
18
|
|
|
19
|
-
Use narrow identifiers, direction, and depth limits when the task concerns one chain. Broad scans build the structural event graph without expanding every scripted helper into duplicate paths.
|
|
19
|
+
Use narrow identifiers, direction, and depth limits when the task concerns one chain. Broad scans build the structural event graph without expanding every scripted helper into duplicate paths. Game-backed or other large workspaces automatically use a focused source profile containing event definitions and on-actions, then defer workspace-wide helper projections and lifecycle passes. The result is marked partial and records that boundary in the linked evidence instead of waiting until the MCP transport times out. Focused trace and path calls collapse only the selected bounded route.
|
|
20
20
|
|
|
21
|
-
The compact response summarizes the result. Small scan resources contain the complete graph;
|
|
21
|
+
The compact response summarizes the result. Small scan resources contain the complete graph; large scan resources contain exact totals, grouped inventories, representative diagnostics, and the revision needed for focused follow-up queries. Large focused scans keep direct event and on-action evidence, call sites, state accesses, and source locations while leaving helper expansion and workspace-wide lifecycle analysis explicit as deferred boundaries. This keeps an agent's prompt and artifact storage bounded without allowing a broad scan to exhaust the MCP process.
|
|
22
22
|
|
|
23
23
|
Selectors use one of these forms:
|
|
24
24
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hoi4-agent-tools",
|
|
3
3
|
"mcpName": "io.github.klimPaskov/hoi4-agent-tools",
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.5",
|
|
5
5
|
"description": "Analyze HOI4 events, tech trees, AI weights, and MTTH; build focus trees, GUIs, and maps.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/configuration.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/continuous-focus-palette.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/custom-weighted-pool.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/event-feature-manifest.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/focus-plan.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/focus-planning-sidecar.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/gui-animation-source.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/gui-helper.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/gui-scenario.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/map-operation.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"oneOf": [
|
|
5
5
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/operation-result.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-analysis-result.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-compare-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-evaluate-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-inspect-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-render-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-scenario-set.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-sequence-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-simulate-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.3.5/schemas/probability-sweep-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.klimPaskov/hoi4-agent-tools",
|
|
4
4
|
"title": "HOI4 Agent Tools",
|
|
5
5
|
"description": "Analyze HOI4 events, tech trees, AI weights, and MTTH; build focus trees, GUIs, and maps.",
|
|
6
|
-
"version": "2.3.
|
|
6
|
+
"version": "2.3.5",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "https://github.com/klimPaskov/hoi4-agent-tools",
|
|
9
9
|
"source": "github"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"identifier": "hoi4-agent-tools",
|
|
16
|
-
"version": "2.3.
|
|
16
|
+
"version": "2.3.5",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
}
|