hoi4-agent-tools 4.0.5 → 4.2.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 +32 -0
- package/README.md +2 -0
- package/dist/hoi4_agent_tools/core/bounded-source.js +210 -0
- package/dist/hoi4_agent_tools/core/canonical.js +42 -0
- package/dist/hoi4_agent_tools/core/condition-evaluator.js +12 -8
- package/dist/hoi4_agent_tools/core/dependency-walk.js +21 -2
- package/dist/hoi4_agent_tools/core/diagnostic-rollup.js +91 -0
- package/dist/hoi4_agent_tools/core/domain-validation.js +12 -0
- package/dist/hoi4_agent_tools/core/engine.js +2 -1
- package/dist/hoi4_agent_tools/core/impact-graph.js +6 -2
- package/dist/hoi4_agent_tools/core/impact-semantic.js +12 -2
- package/dist/hoi4_agent_tools/core/index.js +1 -0
- package/dist/hoi4_agent_tools/core/job-executor.js +9 -0
- package/dist/hoi4_agent_tools/core/job-worker-host.js +12 -2
- package/dist/hoi4_agent_tools/core/managed-outputs.js +72 -0
- package/dist/hoi4_agent_tools/core/operation-result.js +28 -2
- package/dist/hoi4_agent_tools/core/scanner.js +17 -0
- package/dist/hoi4_agent_tools/core/server-state.js +9 -1
- package/dist/hoi4_agent_tools/core/temporary-directories.js +5 -1
- package/dist/hoi4_agent_tools/core/transaction-canonical.js +43 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.js +7 -2
- package/dist/hoi4_agent_tools/core/transactions.js +355 -111
- package/dist/hoi4_agent_tools/decision/service.js +22 -1
- package/dist/hoi4_agent_tools/event/algorithms.js +3 -2
- package/dist/hoi4_agent_tools/event/graph.js +52 -35
- package/dist/hoi4_agent_tools/event/limits.js +4 -0
- package/dist/hoi4_agent_tools/event/operations.js +11 -0
- package/dist/hoi4_agent_tools/event/queries.js +13 -34
- package/dist/hoi4_agent_tools/event/render.js +4 -2
- package/dist/hoi4_agent_tools/event/service.js +93 -33
- package/dist/hoi4_agent_tools/event/source-analysis.js +94 -4
- package/dist/hoi4_agent_tools/event/state-keys.js +63 -0
- package/dist/hoi4_agent_tools/event/summary.js +130 -0
- package/dist/hoi4_agent_tools/impact/service.js +2 -1
- package/dist/hoi4_agent_tools/map/derived.js +21 -6
- package/dist/hoi4_agent_tools/map/job-operations.js +9 -3
- package/dist/hoi4_agent_tools/map/operations.js +72 -21
- package/dist/hoi4_agent_tools/map/reference-labels.js +238 -0
- package/dist/hoi4_agent_tools/map/reference-preparation.js +247 -0
- package/dist/hoi4_agent_tools/map/reference-world.js +1097 -0
- package/dist/hoi4_agent_tools/map/render.js +9 -1
- package/dist/hoi4_agent_tools/map/service.js +222 -18
- package/dist/hoi4_agent_tools/map/tool-operations.js +71 -2
- package/dist/hoi4_agent_tools/map/world-context.js +148 -0
- package/dist/hoi4_agent_tools/map/world-drainage.js +273 -0
- package/dist/hoi4_agent_tools/map/world-outputs.js +55 -0
- package/dist/hoi4_agent_tools/map/world-preview-style.js +157 -0
- package/dist/hoi4_agent_tools/map/world-preview.js +230 -0
- package/dist/hoi4_agent_tools/map/world-provinces.js +524 -0
- package/dist/hoi4_agent_tools/map/world.js +374 -253
- package/dist/hoi4_agent_tools/mcp/tools/analysis.js +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/event.js +66 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js +15 -2
- package/dist/hoi4_agent_tools/reference/source-lookup.js +4 -2
- package/dist/hoi4_agent_tools/schemas/map-requests.js +4 -0
- package/dist/hoi4_agent_tools/schemas/map.js +103 -10
- package/dist/hoi4_agent_tools/schemas/transaction.js +20 -3
- package/dist/hoi4_agent_tools/version.js +2 -2
- package/docs/analysis.md +4 -0
- package/docs/events.md +12 -0
- package/docs/images/reference-maps/political.png +0 -0
- package/docs/images/reference-maps/sketch.png +0 -0
- package/docs/map.md +45 -0
- package/docs/third-party-notices.md +6 -0
- package/npm-shrinkwrap.json +124 -2
- package/package.json +4 -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/decision-inspect-input.schema.json +1 -1
- package/schemas/error-log-input.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/helper-expansion-request.schema.json +1 -1
- package/schemas/helper-expansion-summary.schema.json +1 -1
- package/schemas/impact-inspect-input.schema.json +1 -1
- package/schemas/map-operation.schema.json +373 -70
- package/schemas/mechanic-test-input.schema.json +1 -1
- package/schemas/mod-index-input.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/package-check-input.schema.json +1 -1
- package/schemas/package-manifest.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/schemas/reference-context-input.schema.json +1 -1
- package/schemas/reference-read-input.schema.json +1 -1
- package/schemas/reference-search-input.schema.json +1 -1
- package/schemas/scenario-suite.schema.json +1 -1
- package/schemas/scenario-test-input.schema.json +1 -1
- package/schemas/script-validate-input.schema.json +1 -1
- package/schemas/shared-scenario.schema.json +1 -1
- package/schemas/source-lookup-input.schema.json +1 -1
- package/server.json +2 -2
|
@@ -16,6 +16,7 @@ const impactOutputSchema = strictOperationResultSchema(z
|
|
|
16
16
|
directConsumers: nonNegativeIntegerSchema,
|
|
17
17
|
transitiveConsumers: nonNegativeIntegerSchema,
|
|
18
18
|
affectedFiles: nonNegativeIntegerSchema,
|
|
19
|
+
affectedFilePaths: z.array(z.string().max(1024)).max(64).optional(),
|
|
19
20
|
cycles: nonNegativeIntegerSchema,
|
|
20
21
|
scenarioSuites: nonNegativeIntegerSchema,
|
|
21
22
|
affectedCases: nonNegativeIntegerSchema,
|
|
@@ -70,6 +70,70 @@ const renderHashSchema = z
|
|
|
70
70
|
html: sha256Schema.optional(),
|
|
71
71
|
})
|
|
72
72
|
.strict();
|
|
73
|
+
const countMapSchema = z.record(z.string().max(256), nonNegativeIntegerSchema);
|
|
74
|
+
const inlineIdsSchema = z.array(z.string().max(256)).max(20);
|
|
75
|
+
/** Bounded inline answer of an inspection; see docs/events.md. */
|
|
76
|
+
const eventInspectSummarySchema = z
|
|
77
|
+
.object({
|
|
78
|
+
issues: nonNegativeIntegerSchema.optional(),
|
|
79
|
+
byClassification: countMapSchema.optional(),
|
|
80
|
+
byLayer: countMapSchema.optional(),
|
|
81
|
+
topCodes: countMapSchema.optional(),
|
|
82
|
+
findings: z
|
|
83
|
+
.array(z
|
|
84
|
+
.object({
|
|
85
|
+
code: z.string().max(256),
|
|
86
|
+
classification: z.enum([
|
|
87
|
+
'confirmed_error',
|
|
88
|
+
'probable_defect',
|
|
89
|
+
'design_warning',
|
|
90
|
+
'unresolved_analysis',
|
|
91
|
+
]),
|
|
92
|
+
confidence: z.enum(['confirmed', 'high', 'medium', 'low', 'unresolved']),
|
|
93
|
+
message: z.string().max(300),
|
|
94
|
+
path: z.string().max(1024).optional(),
|
|
95
|
+
line: nonNegativeIntegerSchema.optional(),
|
|
96
|
+
subject: z.string().max(1024).optional(),
|
|
97
|
+
})
|
|
98
|
+
.strict())
|
|
99
|
+
.max(10)
|
|
100
|
+
.optional(),
|
|
101
|
+
trace: z
|
|
102
|
+
.object({
|
|
103
|
+
nodes: nonNegativeIntegerSchema,
|
|
104
|
+
edges: nonNegativeIntegerSchema,
|
|
105
|
+
events: nonNegativeIntegerSchema,
|
|
106
|
+
eventIds: inlineIdsSchema,
|
|
107
|
+
boundaryNodes: nonNegativeIntegerSchema,
|
|
108
|
+
truncated: z.boolean(),
|
|
109
|
+
})
|
|
110
|
+
.strict()
|
|
111
|
+
.optional(),
|
|
112
|
+
entryPoints: nonNegativeIntegerSchema.optional(),
|
|
113
|
+
entryKinds: countMapSchema.optional(),
|
|
114
|
+
automaticEvents: nonNegativeIntegerSchema.optional(),
|
|
115
|
+
callerlessTriggeredEvents: nonNegativeIntegerSchema.optional(),
|
|
116
|
+
callerlessTriggeredByLayer: countMapSchema.optional(),
|
|
117
|
+
callerlessTriggeredEventIds: inlineIdsSchema.optional(),
|
|
118
|
+
cycles: nonNegativeIntegerSchema.optional(),
|
|
119
|
+
found: z.boolean().optional(),
|
|
120
|
+
steps: nonNegativeIntegerSchema.optional(),
|
|
121
|
+
eventIds: inlineIdsSchema.optional(),
|
|
122
|
+
failureReason: z.string().max(64).optional(),
|
|
123
|
+
failureMessage: z.string().max(1024).optional(),
|
|
124
|
+
unresolvedAssumptions: nonNegativeIntegerSchema.optional(),
|
|
125
|
+
direct: nonNegativeIntegerSchema.optional(),
|
|
126
|
+
upstream: nonNegativeIntegerSchema.optional(),
|
|
127
|
+
downstream: nonNegativeIntegerSchema.optional(),
|
|
128
|
+
stateAccesses: nonNegativeIntegerSchema.optional(),
|
|
129
|
+
affectedRoots: nonNegativeIntegerSchema.optional(),
|
|
130
|
+
affectedTerminals: nonNegativeIntegerSchema.optional(),
|
|
131
|
+
wouldDisconnect: nonNegativeIntegerSchema.optional(),
|
|
132
|
+
wouldDisconnectTerminals: nonNegativeIntegerSchema.optional(),
|
|
133
|
+
unresolved: nonNegativeIntegerSchema.optional(),
|
|
134
|
+
sampleDownstream: z.array(z.string().max(1024)).max(20).optional(),
|
|
135
|
+
})
|
|
136
|
+
.strict();
|
|
73
137
|
const eventInspectDataSchema = z
|
|
74
138
|
.object({
|
|
75
139
|
mode: eventInspectModeSchema,
|
|
@@ -78,6 +142,7 @@ const eventInspectDataSchema = z
|
|
|
78
142
|
graphHash: sha256Schema,
|
|
79
143
|
counts: eventGraphCountsSchema,
|
|
80
144
|
helperExpansion: helperExpansionSummarySchema.optional(),
|
|
145
|
+
summary: eventInspectSummarySchema.optional(),
|
|
81
146
|
boundary: inspectBoundarySchema,
|
|
82
147
|
})
|
|
83
148
|
.strict();
|
|
@@ -93,6 +158,7 @@ const eventRenderOutputSchema = strictOperationResultSchema(z
|
|
|
93
158
|
selectedNodes: nonNegativeIntegerSchema,
|
|
94
159
|
omittedNodes: nonNegativeIntegerSchema,
|
|
95
160
|
branchRenders: nonNegativeIntegerSchema,
|
|
161
|
+
omittedBranchRenders: nonNegativeIntegerSchema,
|
|
96
162
|
}),
|
|
97
163
|
boundary: z
|
|
98
164
|
.object({
|
|
@@ -105,7 +105,7 @@ const mapRenderDataSchema = z
|
|
|
105
105
|
const mapInspectOutputSchema = strictOperationResultSchema(z.union([mapWorkspaceInspectDataSchema, mapRenderDataSchema]));
|
|
106
106
|
const mapPlanOutputSchema = strictOperationResultSchema(z
|
|
107
107
|
.object({
|
|
108
|
-
execution: z.enum(['applied', 'blocked', 'unchanged']),
|
|
108
|
+
execution: z.enum(['applied', 'blocked', 'unchanged', 'preview']),
|
|
109
109
|
allocations: z.array(allocationEvidenceSchema).max(100),
|
|
110
110
|
operationBlockers: z.array(operationBlockerDataSchema).max(100),
|
|
111
111
|
expectedChangedBounds: changedBoundsSchema.nullable(),
|
|
@@ -116,6 +116,19 @@ const mapPlanOutputSchema = strictOperationResultSchema(z
|
|
|
116
116
|
semanticDiffTruncated: z.boolean(),
|
|
117
117
|
fileCount: nonNegativeIntegerSchema,
|
|
118
118
|
artifactCount: nonNegativeIntegerSchema,
|
|
119
|
+
preview: z
|
|
120
|
+
.object({
|
|
121
|
+
offlineRepresentation: z.literal(true),
|
|
122
|
+
width: nonNegativeIntegerSchema,
|
|
123
|
+
height: nonNegativeIntegerSchema,
|
|
124
|
+
provinces: nonNegativeIntegerSchema,
|
|
125
|
+
states: nonNegativeIntegerSchema,
|
|
126
|
+
regions: nonNegativeIntegerSchema,
|
|
127
|
+
countries: nonNegativeIntegerSchema,
|
|
128
|
+
warningCount: nonNegativeIntegerSchema,
|
|
129
|
+
})
|
|
130
|
+
.strict()
|
|
131
|
+
.optional(),
|
|
119
132
|
})
|
|
120
133
|
.strict());
|
|
121
134
|
const artifactProducing = {
|
|
@@ -136,7 +149,7 @@ export const mapTaskTools = [
|
|
|
136
149
|
{
|
|
137
150
|
name: 'hoi4.map_rewrite',
|
|
138
151
|
title: 'Build a world or edit the map',
|
|
139
|
-
description: 'Build a world
|
|
152
|
+
description: 'Build a world from a local reference image or edit provinces, states, regions and supply. Example: {"preview":true,"operations":[{"kind":"create_world","reference":{"imagePath":"references/world.png"}}]}. Preview first to check coastlines, terrain and provisional countries; omit preview to write the generated map and starting scenario. Reviewed masks and labels refine ambiguous artwork. Returns counts, diagnostics and linked PNGs. Offline validation does not prove game playability.',
|
|
140
153
|
inputSchema: mapRewriteRequestSchema,
|
|
141
154
|
outputSchema: mapPlanOutputSchema,
|
|
142
155
|
annotations: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ServiceError } from '../core/result.js';
|
|
2
2
|
import { decodeSource } from '../core/source/encoding.js';
|
|
3
3
|
import { readBoundedJsonLines } from './lines.js';
|
|
4
|
-
import { scanImpactSemanticReferences } from '../core/impact-semantic.js';
|
|
4
|
+
import { IMPACT_SEMANTIC_MAX_REFERENCES, scanImpactSemanticReferences, } from '../core/impact-semantic.js';
|
|
5
5
|
import { parseClausewitz } from '../core/source/parser.js';
|
|
6
6
|
import { navigateSource } from './source-navigation.js';
|
|
7
7
|
import { locatePatterns, locateSource } from './source-locate.js';
|
|
@@ -126,7 +126,9 @@ export async function sourceLookup(engine, workspaceId, input, principal, signal
|
|
|
126
126
|
});
|
|
127
127
|
let semantic = input.includeReferences ? semanticCache.get(snapshot) : undefined;
|
|
128
128
|
if (input.includeReferences && semantic === undefined) {
|
|
129
|
-
semantic = scanImpactSemanticReferences(snapshot,
|
|
129
|
+
semantic = scanImpactSemanticReferences(snapshot, IMPACT_SEMANTIC_MAX_REFERENCES, {
|
|
130
|
+
includeOnActions: true,
|
|
131
|
+
});
|
|
130
132
|
semanticCache.set(snapshot, semantic);
|
|
131
133
|
}
|
|
132
134
|
const referenceIdentities = new Set();
|
|
@@ -143,6 +143,10 @@ export const mapRewriteRequestSchema = z
|
|
|
143
143
|
.object({
|
|
144
144
|
workspaceId: workspaceIdSchema,
|
|
145
145
|
operations: z.array(compactMapOperationSchema).min(1).max(100),
|
|
146
|
+
preview: z
|
|
147
|
+
.boolean()
|
|
148
|
+
.default(false)
|
|
149
|
+
.describe('Return generated reference, terrain and political previews and counts without changing mod files.'),
|
|
146
150
|
diffScale: z.number().int().min(1).max(16).optional(),
|
|
147
151
|
requestKey: backgroundRequestKeySchema.optional(),
|
|
148
152
|
})
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
2
|
import { renderDimensionViolation, RENDER_MAX_DIMENSION } from '../core/render-budget.js';
|
|
3
3
|
import { MAP_MASK_CELL_LIMIT, MAP_SELECTED_PIXEL_LIMIT } from '../map/limits.js';
|
|
4
|
+
import { workspaceRelativePathSchema } from './common.js';
|
|
4
5
|
const identifier = z.string().min(1).max(256);
|
|
6
|
+
// eslint-disable-next-line no-control-regex -- portable JSON Schema pattern rejects grammar-breaking controls.
|
|
7
|
+
const worldNamePattern = /^[^\u0000-\u001f\u007f]+$/u;
|
|
8
|
+
const worldName = z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(64)
|
|
12
|
+
.regex(worldNamePattern, 'World names must not contain control characters');
|
|
5
13
|
const stateId = z.number().int().positive();
|
|
6
14
|
const provinceId = z.number().int().min(0);
|
|
7
15
|
const provinceTypeSchema = z.enum(['land', 'sea', 'lake']);
|
|
@@ -18,6 +26,83 @@ const operationBase = {
|
|
|
18
26
|
id: identifier,
|
|
19
27
|
summary: z.string().max(1000).optional(),
|
|
20
28
|
};
|
|
29
|
+
const referencePointSchema = z
|
|
30
|
+
.object({ x: z.number().min(0).max(1), y: z.number().min(0).max(1) })
|
|
31
|
+
.strict();
|
|
32
|
+
const referenceKindSchema = z.enum([
|
|
33
|
+
'sea',
|
|
34
|
+
'land',
|
|
35
|
+
'plains',
|
|
36
|
+
'forest',
|
|
37
|
+
'desert',
|
|
38
|
+
'mountain',
|
|
39
|
+
'snow',
|
|
40
|
+
'lake',
|
|
41
|
+
'volcano',
|
|
42
|
+
'hills',
|
|
43
|
+
'marsh',
|
|
44
|
+
'jungle',
|
|
45
|
+
]);
|
|
46
|
+
const referencePolygonSchema = z.array(referencePointSchema).min(3).max(1_000);
|
|
47
|
+
const referenceImagePathSchema = workspaceRelativePathSchema.refine((value) => !value.includes(':') && /\.(?:png|jpe?g|webp)$/iu.test(value), 'Reference must be a workspace-relative PNG, JPEG or WebP file');
|
|
48
|
+
/** Optional controls refine automatic interpretation; a single imagePath is sufficient. */
|
|
49
|
+
export const worldReferenceSchema = z
|
|
50
|
+
.object({
|
|
51
|
+
imagePath: referenceImagePathSchema,
|
|
52
|
+
politicalImagePath: referenceImagePathSchema.optional(),
|
|
53
|
+
terrainImagePath: referenceImagePathSchema.optional(),
|
|
54
|
+
heightImagePath: referenceImagePathSchema.optional(),
|
|
55
|
+
mode: z
|
|
56
|
+
.enum(['auto', 'illustrated', 'satellite', 'sketch', 'political', 'grayscale'])
|
|
57
|
+
.optional(),
|
|
58
|
+
crop: z
|
|
59
|
+
.object({
|
|
60
|
+
x: z.number().int().min(0),
|
|
61
|
+
y: z.number().int().min(0),
|
|
62
|
+
width: z.number().int().positive().max(65_536),
|
|
63
|
+
height: z.number().int().positive().max(65_536),
|
|
64
|
+
})
|
|
65
|
+
.strict()
|
|
66
|
+
.optional(),
|
|
67
|
+
ignoreMasks: z.array(referencePolygonSchema).max(64).optional(),
|
|
68
|
+
annotations: z
|
|
69
|
+
.array(z.object({ kind: referenceKindSchema, polygon: referencePolygonSchema }).strict())
|
|
70
|
+
.max(128)
|
|
71
|
+
.optional(),
|
|
72
|
+
riverAnnotations: z.array(z.array(referencePointSchema).min(2).max(1_000)).max(128).optional(),
|
|
73
|
+
palette: z
|
|
74
|
+
.array(z.object({ kind: referenceKindSchema, color: rgbSchema }).strict())
|
|
75
|
+
.max(64)
|
|
76
|
+
.optional(),
|
|
77
|
+
coastDetail: z.number().min(0).max(1).optional(),
|
|
78
|
+
labels: z
|
|
79
|
+
.array(z
|
|
80
|
+
.object({
|
|
81
|
+
name: worldName,
|
|
82
|
+
x: z.number().min(0).max(1),
|
|
83
|
+
y: z.number().min(0).max(1),
|
|
84
|
+
kind: z.enum(['state', 'country', 'city', 'sea', 'region', 'mountain', 'geography']),
|
|
85
|
+
})
|
|
86
|
+
.strict())
|
|
87
|
+
.max(2_048)
|
|
88
|
+
.optional(),
|
|
89
|
+
cities: z
|
|
90
|
+
.array(z
|
|
91
|
+
.object({
|
|
92
|
+
name: worldName,
|
|
93
|
+
x: z.number().min(0).max(1),
|
|
94
|
+
y: z.number().min(0).max(1),
|
|
95
|
+
countryTag: z
|
|
96
|
+
.string()
|
|
97
|
+
.regex(/^[A-Z][A-Z0-9]{2}$/u)
|
|
98
|
+
.optional(),
|
|
99
|
+
})
|
|
100
|
+
.strict())
|
|
101
|
+
.max(200)
|
|
102
|
+
.optional(),
|
|
103
|
+
})
|
|
104
|
+
.strict()
|
|
105
|
+
.describe('Generate from one local reference image. Optional controls use normalized 0..1 source coordinates; crop uses source pixels. Labels and political semantics may need refinement.');
|
|
21
106
|
const moveStateDistributionSchema = z
|
|
22
107
|
.object({
|
|
23
108
|
stateValues: z.enum(['retain-in-current-states', 'proportional-by-land-pixels']),
|
|
@@ -473,8 +558,15 @@ export const mapOperationSchema = z.discriminatedUnion('kind', [
|
|
|
473
558
|
.object({
|
|
474
559
|
...operationBase,
|
|
475
560
|
kind: z.literal('create_world'),
|
|
476
|
-
|
|
477
|
-
|
|
561
|
+
worldName: worldName.optional(),
|
|
562
|
+
startingScenario: z
|
|
563
|
+
.boolean()
|
|
564
|
+
.optional()
|
|
565
|
+
.describe('Include basic starting units and stockpiles and replace old-world startup/AI geography. Defaults on for reference-image worlds.'),
|
|
566
|
+
id: identifier.default('world'),
|
|
567
|
+
reference: worldReferenceSchema.optional(),
|
|
568
|
+
width: z.number().int().min(256).max(8_192).multipleOf(256).optional(),
|
|
569
|
+
height: z.number().int().min(256).max(4_096).multipleOf(256).optional(),
|
|
478
570
|
seed: z.number().int().min(0).max(2_147_483_647).default(1),
|
|
479
571
|
landCoverage: z.number().min(0.05).max(0.9).default(0.35),
|
|
480
572
|
continents: z.number().int().min(1).max(12).default(3),
|
|
@@ -485,28 +577,29 @@ export const mapOperationSchema = z.discriminatedUnion('kind', [
|
|
|
485
577
|
.describe('Land outlines in top-left pixel coordinates; omit for generated continents.'),
|
|
486
578
|
landProvincePixels: z.number().int().min(60).max(50_000).default(400),
|
|
487
579
|
seaProvincePixels: z.number().int().min(200).max(200_000).default(2_500),
|
|
488
|
-
provincesPerState: z.number().int().min(1).max(64).
|
|
489
|
-
landProvincesPerRegion: z.number().int().min(2).max(400).
|
|
490
|
-
seaProvincesPerRegion: z.number().int().min(1).max(200).
|
|
580
|
+
provincesPerState: z.number().int().min(1).max(64).optional(),
|
|
581
|
+
landProvincesPerRegion: z.number().int().min(2).max(400).optional(),
|
|
582
|
+
seaProvincesPerRegion: z.number().int().min(1).max(200).optional(),
|
|
491
583
|
countries: z
|
|
492
584
|
.array(z
|
|
493
585
|
.object({
|
|
494
586
|
tag: z.string().regex(/^[A-Z][A-Z0-9]{2}$/u),
|
|
495
|
-
name:
|
|
496
|
-
adjective:
|
|
587
|
+
name: worldName,
|
|
588
|
+
adjective: worldName.optional(),
|
|
497
589
|
color: rgbSchema.optional(),
|
|
498
590
|
share: z.number().min(0.05).max(100).optional(),
|
|
499
591
|
ideology: z.enum(['democratic', 'communism', 'fascism', 'neutrality']).optional(),
|
|
500
592
|
})
|
|
501
593
|
.strict())
|
|
502
594
|
.min(1)
|
|
503
|
-
.max(200)
|
|
504
|
-
|
|
595
|
+
.max(200)
|
|
596
|
+
.optional(),
|
|
597
|
+
continentNames: z.array(worldName).max(12).optional(),
|
|
505
598
|
startDate: z
|
|
506
599
|
.string()
|
|
507
600
|
.regex(/^\d{3,4}\.\d{1,2}\.\d{1,2}$/u)
|
|
508
601
|
.default('1936.1.1'),
|
|
509
|
-
rivers: z.number().int().min(0).max(500).
|
|
602
|
+
rivers: z.number().int().min(0).max(500).optional(),
|
|
510
603
|
replaceBaseGame: z
|
|
511
604
|
.boolean()
|
|
512
605
|
.default(true)
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
|
-
import { TRANSACTION_MAX_APPLIED_FILES, TRANSACTION_MAX_ARTIFACTS, TRANSACTION_MAX_DIAGNOSTICS, TRANSACTION_MAX_FILES, TRANSACTION_MAX_OPERATIONS, TRANSACTION_MAX_READ_DEPENDENCIES, TRANSACTION_MAX_VALIDATION_CHECKS, } from '../core/transaction-limits.js';
|
|
2
|
+
import { TRANSACTION_MAX_APPLIED_FILES, TRANSACTION_MAX_ARTIFACTS, TRANSACTION_MAX_DIAGNOSTICS, TRANSACTION_MAX_FILES, TRANSACTION_MAX_OPERATIONS, TRANSACTION_MAX_PENDING_FILES, TRANSACTION_MAX_READ_DEPENDENCIES, TRANSACTION_MAX_VALIDATION_CHECKS, } from '../core/transaction-limits.js';
|
|
3
3
|
import { TRANSACTION_VERSION } from '../version.js';
|
|
4
4
|
import { diagnosticSchema, workspaceIdSchema, workspaceRelativePathSchema } from './common.js';
|
|
5
5
|
const sha256Schema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
6
|
+
const compatibleTransactionVersion = z.union([z.literal(1), z.literal(TRANSACTION_VERSION)]);
|
|
7
|
+
const fileIdentitySchema = z
|
|
8
|
+
.object({
|
|
9
|
+
device: z.string().regex(/^(0|[1-9][0-9]{0,39})$/u),
|
|
10
|
+
inode: z.string().regex(/^(0|[1-9][0-9]{0,39})$/u),
|
|
11
|
+
})
|
|
12
|
+
.strict();
|
|
6
13
|
const identifierSchema = z.string().min(1).max(256);
|
|
7
14
|
const summarySchema = z.string().max(4096);
|
|
8
15
|
const mediaTypeSchema = z.string().min(1).max(255);
|
|
@@ -55,7 +62,7 @@ const transactionFileSchema = z
|
|
|
55
62
|
.strict();
|
|
56
63
|
const transactionPlanPayloadSchema = z
|
|
57
64
|
.object({
|
|
58
|
-
version:
|
|
65
|
+
version: compatibleTransactionVersion,
|
|
59
66
|
workspaceId: workspaceIdSchema,
|
|
60
67
|
principal: z.string().max(1024).nullable(),
|
|
61
68
|
rootFingerprint: sha256Schema,
|
|
@@ -72,7 +79,7 @@ const transactionPlanPayloadSchema = z
|
|
|
72
79
|
.strict();
|
|
73
80
|
export const transactionManifestSchema = z
|
|
74
81
|
.object({
|
|
75
|
-
version:
|
|
82
|
+
version: compatibleTransactionVersion,
|
|
76
83
|
revision: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
|
|
77
84
|
transactionId: z.string().regex(/^txn_[0-9a-f-]{36}$/u),
|
|
78
85
|
workspaceId: workspaceIdSchema,
|
|
@@ -98,6 +105,16 @@ export const transactionManifestSchema = z
|
|
|
98
105
|
artifacts: z.array(artifactLinkSchema).max(TRANSACTION_MAX_ARTIFACTS),
|
|
99
106
|
executionArtifacts: z.array(artifactLinkSchema).max(TRANSACTION_MAX_ARTIFACTS).optional(),
|
|
100
107
|
appliedFiles: z.array(workspaceRelativePathSchema).max(TRANSACTION_MAX_APPLIED_FILES),
|
|
108
|
+
pendingFiles: z
|
|
109
|
+
.array(z
|
|
110
|
+
.object({
|
|
111
|
+
relativePath: workspaceRelativePathSchema,
|
|
112
|
+
beforeIdentity: fileIdentitySchema.nullable(),
|
|
113
|
+
stagedIdentity: fileIdentitySchema.nullable(),
|
|
114
|
+
})
|
|
115
|
+
.strict())
|
|
116
|
+
.max(TRANSACTION_MAX_PENDING_FILES)
|
|
117
|
+
.optional(),
|
|
101
118
|
rollbackStatus: z.enum(['available', 'applied', 'failed']),
|
|
102
119
|
failure: z.object({ code: identifierSchema, message: summarySchema }).strict().optional(),
|
|
103
120
|
})
|
|
@@ -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 = '4.0
|
|
3
|
+
export const PACKAGE_VERSION = '4.2.0';
|
|
4
4
|
export const MCP_PROTOCOL_VERSION = LATEST_PROTOCOL_VERSION;
|
|
5
5
|
export const CONFIG_VERSION = 1;
|
|
6
|
-
export const TRANSACTION_VERSION =
|
|
6
|
+
export const TRANSACTION_VERSION = 2;
|
package/docs/analysis.md
CHANGED
|
@@ -58,6 +58,8 @@ Malformed, missing, or oversized suite files produce explicit partial coverage.
|
|
|
58
58
|
The graph includes active and overridden definitions, direct and transitive consumers, source locations, state-key reads and writes, and coverage limits.
|
|
59
59
|
It connects source-backed event, focus, decision, idea, technology, scripted-helper, localisation, GUI, sprite, and texture references where a typed static edge can be established.
|
|
60
60
|
Unresolved dynamic names, missing definitions, skipped sources, cycles, and traversal limits remain explicit in the report.
|
|
61
|
+
A dynamic reference counts against completeness only when it names a symbol kind the traversal reaches, so `activate_decision = event_target:choice` affects the impact of a decision but not of an idea that no decision consumes.
|
|
62
|
+
`data.affectedFilePaths` lists up to 64 affected files; the result's `changedFiles` stays empty because the analysis never writes.
|
|
61
63
|
An affected file is a source dependency lead, not a claim that the game executed that path.
|
|
62
64
|
|
|
63
65
|
## Decisions and missions
|
|
@@ -99,6 +101,8 @@ The inventory lists active decisions, category fragments, overridden source defi
|
|
|
99
101
|
Category and decision `allowed`, `visible`, and `available` results remain separate; mission visibility is reported as ignored where documented.
|
|
100
102
|
Target selection covers explicit lists and arrays plus supported state ownership, control, and continent filters.
|
|
101
103
|
When a target, flag, variable, or helper input is unknown, the corresponding gate remains unresolved.
|
|
104
|
+
This includes `has_variable` and `has_event_target`: an undeclared variable or event target is unknown, not absent, unless the scenario sets `closedFlags: true`.
|
|
105
|
+
AI weights use probability adapters verified for one game version. On another installed version the report keeps the gate, cost and lifecycle evidence, marks the AI part unresolved and returns a `PROBABILITY_GAME_VERSION_UNSUPPORTED` warning.
|
|
102
106
|
|
|
103
107
|
Costs distinguish automatic engine political-power payment from a custom affordability trigger and its separate `complete_effect` payment.
|
|
104
108
|
The report identifies direct deductions, possible double payment, missing direct payment, conditional or helper-dependent paths, repeatability, cooldowns, removal, activation, cancellation, and mission timeout paths.
|
package/docs/events.md
CHANGED
|
@@ -26,6 +26,18 @@ For a large helper closure, use [bounded helper expansion](helper-expansion.md)
|
|
|
26
26
|
|
|
27
27
|
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.
|
|
28
28
|
|
|
29
|
+
`data.summary` answers the request inline before the linked report is opened. `scan`, `lint` and `state_flow` return the number of findings in the selection, counts by classification, the ten most frequent codes and the ten most severe findings with their path, line and subject. `trace` adds the traced node, edge and event counts with up to twenty event IDs. `roots` returns entry-point counts by kind, automatic and callerless triggered events, and cycle counts. `data.counts` still describes the whole analyzed graph, including vanilla events.
|
|
30
|
+
|
|
31
|
+
`explain_path` adds whether a route was found, its step count, event IDs and failure reason; `impact` adds direct, upstream, downstream, affected-root and disconnection counts.
|
|
32
|
+
|
|
33
|
+
Workspace-wide modes expand scripted helpers up to a fixed work ceiling. On a very large mod the expansion stops there with an `EVENT_HELPER_PROJECTION_LIMIT` finding that names the completed and total roots; calls from the remaining roots stay structural, and a namespace, event or file selector or `helper_expansion` mode covers them. An unselected overview render returns the overview and as many branch renders as the request's raster budget allows, reporting the rest in `omittedBranchRenders`.
|
|
34
|
+
|
|
35
|
+
A `scan` retains the complete graph as a snapshot artifact only up to 128 MiB of canonical JSON. A larger graph, such as a mod with thousands of events and hundreds of thousands of state accesses, returns the scan report with the info diagnostic `EVENT_GRAPH_SNAPSHOT_OMITTED`; comparisons then use the revisions this server holds in memory.
|
|
36
|
+
|
|
37
|
+
An assignment such as `custom_effect = yes` is reported as a missing helper only where it can execute: in effect blocks of events, decisions, focuses and on-actions, inside scope and loop effects, and in conventionally named effect blocks (`on_*`, `*_effect`, `*_effects`) of other definition files. Arguments of native effects such as `add_building_construction = { instant_build = yes }`, condition blocks such as `visible`, `available`, `allowed`, `ai_will_do`, `NOT` or `any_*`, and data fields of ideas, raids, special projects and scripted GUIs are not calls.
|
|
38
|
+
|
|
39
|
+
A read is linked to a writer of the same name when the writer is a temporary variable, a write inside a scripted helper (whose scope is decided by its caller), a scope-prefixed name such as `GER.value`, or the array behind `entries^num`. `event_target:name` reads targets saved by `save_event_target_as` and `save_global_event_target_as`; `global_event_target:name` reads only global targets. A finding is kept per owner and subject, so different flags or targets read in one event remain separate findings.
|
|
40
|
+
|
|
29
41
|
Selectors use one of these forms:
|
|
30
42
|
|
|
31
43
|
```json
|
|
Binary file
|
|
Binary file
|
package/docs/map.md
CHANGED
|
@@ -3,6 +3,51 @@
|
|
|
3
3
|
Generate a new world, split states and reshape provinces. Preview the map and update the scripts that refer to it.
|
|
4
4
|
Use `hoi4.map_inspect` and `hoi4.map_rewrite` for provinces, states, regions, supply, railways, positions, ownership, resources, buildings and localisation.
|
|
5
5
|
|
|
6
|
+
## Generate a world from one image
|
|
7
|
+
|
|
8
|
+
Put a PNG, JPEG or WebP reference in your mod workspace, then call `hoi4.map_rewrite`:
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"operations": [{ "kind": "create_world", "reference": { "imagePath": "references/sketch.png" } }]
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
For a first run, copy the project-owned [synthetic hand sketch](images/reference-maps/sketch.png) to `references/sketch.png` in your mod. The [synthetic flat political map](images/reference-maps/political.png) is another ready-made input; copy it to `references/political.png` and change `imagePath` accordingly. The examples are intentionally small and contain no third-party artwork.
|
|
17
|
+
|
|
18
|
+
The server generates the complete map and supporting country/state files in one validated atomic rewrite. No country list, manual masks, coordinates or separate apply call is required. `workspaceId` defaults to the current mod, and the operation ID defaults to `world`. The default reference world is 5632 by 2048 pixels; the artwork retains its aspect ratio within sea padding. Set `width` and `height` to choose other multiples of 256 within the map pixel budget. Reference defaults group 12 provinces per state, 60 land provinces or 30 sea provinces per strategic region, and request 80 rivers; explicit values override these defaults.
|
|
19
|
+
|
|
20
|
+
Add `"preview": true` at the top level to return offline reference, terrain and political PNGs, a comparison and counts without changing mod files. Repeat the request without `preview` to apply it. Preview uses the same extraction, generation and validation as application, and never launches the game.
|
|
21
|
+
|
|
22
|
+
Use a cleaned, unlabelled raster for production generation. Handwriting, sheet joins and map frames can become false islands or relief. Keep the original unchanged, remove these marks in a separate reference, compare its coastlines with the original, then inspect the terrain preview before writing. Supply reviewed names through `reference.labels` and `reference.cities`; an explicit label list, including an empty list, prevents OCR from adding unreviewed names. The server does not automatically redraw artwork or promise lossless interpretation of every map.
|
|
23
|
+
|
|
24
|
+
Reference worlds enable `startingScenario` by default; procedural worlds keep it off unless requested. The starter includes a bookmark, country histories, provisional councils, recruitable infantry templates, two equipped divisions per country, stockpiles, an infantry production line, supply and building positions. `worldName` sets the bookmark title. Demographics, industry and ownership remain provisional. Existing world events, decisions, startup hooks and country-specific AI strategy folders are replaced; shared game systems remain dependencies and can report unused-definition warnings. Generated names use the localisation replacement folder, and explicit country color declarations match the flags. Inherited country tags receive inactive histories. The adjacent launcher `.mod` file must mirror the generated `descriptor.mod` replacement paths. The server cannot enable a launcher playset or test a campaign.
|
|
25
|
+
|
|
26
|
+
Regeneration records generated paths and hashes in `.hoi4-agent-world.json`. It removes obsolete recorded outputs through the same atomic rewrite and refuses to overwrite recorded files edited since generation. Preserve that inventory. Unrelated authored files are not deleted; validation can block a rebuild if they conflict with the generated world. Large world diffs remain linked evidence so counts and preview images fit within the MCP response budget.
|
|
27
|
+
|
|
28
|
+
Automatic interpretation recognizes visual land/water and terrain cues, derives relief and drainage, and uses flat political colors when available. Offline English OCR supplies candidate names and city locations; recognized text bounds are masked before terrain classification so ocean labels do not become islands. Masks near a coastline can soften its detail and produce a review warning. Decorative text, handwriting, unknown languages and visually ambiguous borders may be missed or misclassified; diagnostics identify uncertain interpretation and deterministic defaults. Population, resources, buildings and country ownership inferred from artwork are generated assumptions, not historical facts or a balance assessment.
|
|
29
|
+
|
|
30
|
+
Optional `reference` controls refine the same call:
|
|
31
|
+
|
|
32
|
+
| Field | Meaning |
|
|
33
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `politicalImagePath`, `terrainImagePath`, `heightImagePath` | Additional aligned workspace images for political regions, terrain cues or grayscale height. |
|
|
35
|
+
| `mode` | `auto`, `illustrated`, `satellite`, `sketch`, `political` or `grayscale`. |
|
|
36
|
+
| `crop` | `{x,y,width,height}` in original source pixels. |
|
|
37
|
+
| `ignoreMasks` | Polygons that exclude legends, frames or decorative areas. |
|
|
38
|
+
| `annotations` | `{kind,polygon}` semantic polygons: sea, land, plains, lake, forest, desert, mountain, snow, volcano, hills, marsh or jungle. |
|
|
39
|
+
| `riverAnnotations` | Paths for known rivers. |
|
|
40
|
+
| `palette` | `{kind,color:{r,g,b}}` color cues for the same semantic kinds. |
|
|
41
|
+
| `coastDetail` | Detail preservation from 0 to 1. |
|
|
42
|
+
| `cities` | `{name,x,y,countryTag?}` reviewed city and optional capital anchors. `countryTag` must name a generated country. |
|
|
43
|
+
| `labels` | `{name,x,y,kind}` reviewed names for `state`, `country`, `city`, `sea`, `region`, `mountain` or `geography`; useful for handwritten or ambiguous labels. |
|
|
44
|
+
|
|
45
|
+
Reviewed geography labels preserve names for lakes, rivers, forests and other features in localisation and generated-world provenance. They do not invent a native HOI4 map entity or turn a geographic label into a city or victory point.
|
|
46
|
+
|
|
47
|
+
All annotation positions and polygons use normalized 0 to 1 coordinates relative to the original, uncropped reference, with the origin at its top left. Crop uses source pixels instead. Explicit `countries` on the operation supplies reviewed country tags, names, colors and relative shares. If borders or names cannot be inferred reliably, the server generates named defaults and reports the assumptions. Manual controls are optional and never change the original image.
|
|
48
|
+
|
|
49
|
+
Only relative paths inside the authorized mod workspace are accepted. URLs, absolute paths, traversal, generated storage and symlink escapes are refused. The source is bounded to 1 GiB and 300 million decoded pixels; analysis uses a reduced working PNG up to 3072 pixels per side. The verified source handle is streamed into an exclusive private temporary snapshot, and the same copied bytes are hashed and decoded; the snapshot is removed on completion, failure or cancellation. The original SHA-256 is checked again before writing, using a streaming read, so replacing the source after preparation blocks application. CI image fixtures are synthetic and project-owned. Attributed comparison images may be published separately with permission and remain outside the npm package.
|
|
50
|
+
|
|
6
51
|
## Inspect and navigate the complete map
|
|
7
52
|
|
|
8
53
|
`hoi4.map_inspect` finds map problems and returns a full-map PNG plus a searchable, clickable HTML navigator by default. The navigator supports pan, zoom, fit-to-map, search by numeric ID, localisation key, or localised name, and exact click lookup for province, state, and strategic-region IDs. Hovering reports both top-left bitmap coordinates and bottom-left HOI4 map coordinates.
|
|
@@ -8,4 +8,10 @@ Copyright 2011 The Roboto Project Authors (<https://github.com/googlefonts/robot
|
|
|
8
8
|
|
|
9
9
|
Roboto is licensed under the SIL Open Font License, Version 1.1 (`OFL-1.1`). The complete license text is distributed as `LICENSE` in the installed `@fontsource-variable/roboto` package. The font package is pinned in `package.json` and the published `npm-shrinkwrap.json`; generated SVG/PNG documents may contain derived glyph outlines or pixels as permitted by the OFL.
|
|
10
10
|
|
|
11
|
+
## Offline reference-image OCR
|
|
12
|
+
|
|
13
|
+
`tesseract.js` version 7.0.0 and its `tesseract.js-core` dependency provide the local WebAssembly OCR engine under Apache-2.0. English trained data is installed through `@tesseract.js-data/eng` version 1.0.0. That package declares MIT licensing for its packaging; its upstream [tessdata repository](https://github.com/naptha/tessdata) distributes Tesseract language data under [Apache-2.0](https://github.com/naptha/tessdata/blob/gh-pages/LICENSE). The project uses the unmodified `4.0.0_best_int/eng.traineddata.gz` model from the dependency. No reference artwork or language model is copied into project fixtures or source assets.
|
|
14
|
+
|
|
15
|
+
These dependencies are pinned by `package.json` and `npm-shrinkwrap.json`. OCR uses the installed worker, engine and language bytes without runtime network downloads or external OCR executables. The engine and language data retain their upstream licenses.
|
|
16
|
+
|
|
11
17
|
The application source remains licensed under Apache-2.0 as stated in the repository [LICENSE](../LICENSE). Third-party components retain their respective licenses.
|