hoi4-agent-tools 3.10.0 → 4.0.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 +109 -0
- package/README.md +115 -147
- package/dist/bin/http.js +10 -0
- package/dist/bin/setup.js +3 -2
- package/dist/bin/stdio.js +10 -0
- package/dist/hoi4_agent_tools/core/artifacts.js +40 -5
- package/dist/hoi4_agent_tools/core/canonical.js +18 -4
- package/dist/hoi4_agent_tools/core/configuration.js +7 -0
- package/dist/hoi4_agent_tools/core/domain-scan-patterns.js +12 -0
- package/dist/hoi4_agent_tools/core/domain-tools.js +65 -0
- package/dist/hoi4_agent_tools/core/domain-validation.js +5 -2
- package/dist/hoi4_agent_tools/core/engine.js +29 -2
- package/dist/hoi4_agent_tools/core/index.js +2 -2
- package/dist/hoi4_agent_tools/core/job-executor.js +14 -1
- package/dist/hoi4_agent_tools/core/job-operation-registration.js +15 -7
- package/dist/hoi4_agent_tools/core/job-store.js +78 -5
- package/dist/hoi4_agent_tools/core/job-transactions.js +31 -2
- package/dist/hoi4_agent_tools/core/job-worker-host.js +215 -116
- package/dist/hoi4_agent_tools/core/job-worker.js +90 -36
- package/dist/hoi4_agent_tools/core/operation-result.js +123 -10
- package/dist/hoi4_agent_tools/core/operation-tasks.js +49 -89
- package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +25 -21
- package/dist/hoi4_agent_tools/core/server-registry.js +22 -5
- package/dist/hoi4_agent_tools/core/shared-request-capacity.js +26 -1
- package/dist/hoi4_agent_tools/core/source/cache.js +20 -10
- package/dist/hoi4_agent_tools/core/source/lexer.js +11 -3
- package/dist/hoi4_agent_tools/core/transactions.js +114 -23
- package/dist/hoi4_agent_tools/core/workspace.js +84 -8
- package/dist/hoi4_agent_tools/decision/ai.js +1 -1
- package/dist/hoi4_agent_tools/decision/service.js +28 -0
- package/dist/hoi4_agent_tools/decision/source-inventory.js +7 -5
- package/dist/hoi4_agent_tools/event/job-operations.js +9 -5
- package/dist/hoi4_agent_tools/event/operations.js +14 -1
- package/dist/hoi4_agent_tools/focus/job-operations.js +1 -2
- package/dist/hoi4_agent_tools/focus/tool-operations.js +1 -1
- package/dist/hoi4_agent_tools/gui/validators.js +8 -6
- package/dist/hoi4_agent_tools/map/bmp.js +1 -1
- package/dist/hoi4_agent_tools/map/derived.js +748 -0
- package/dist/hoi4_agent_tools/map/job-operations.js +7 -3
- package/dist/hoi4_agent_tools/map/model.js +103 -0
- package/dist/hoi4_agent_tools/map/operations.js +427 -58
- package/dist/hoi4_agent_tools/map/references.js +483 -0
- package/dist/hoi4_agent_tools/map/service.js +136 -61
- package/dist/hoi4_agent_tools/map/tool-operations.js +2 -1
- package/dist/hoi4_agent_tools/map/validation.js +3 -0
- package/dist/hoi4_agent_tools/map/world.js +1280 -0
- package/dist/hoi4_agent_tools/mcp/server/argument-errors.js +72 -0
- package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +23 -5
- package/dist/hoi4_agent_tools/mcp/server/create.js +7 -0
- package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +24 -30
- package/dist/hoi4_agent_tools/mcp/server/tool-listing.js +45 -0
- package/dist/hoi4_agent_tools/mcp/tools/analysis.js +25 -5
- package/dist/hoi4_agent_tools/mcp/tools/event.js +18 -29
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +4 -12
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +3 -3
- package/dist/hoi4_agent_tools/mcp/tools/job.js +2 -2
- package/dist/hoi4_agent_tools/mcp/tools/map.js +13 -16
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +42 -48
- package/dist/hoi4_agent_tools/mcp/tools/reference.js +22 -5
- package/dist/hoi4_agent_tools/mcp/tools/technology.js +16 -29
- package/dist/hoi4_agent_tools/mcp/transports/http.js +28 -4
- package/dist/hoi4_agent_tools/mcp/transports/stdio-lifetime.js +31 -8
- package/dist/hoi4_agent_tools/probability/job-operations.js +4 -9
- package/dist/hoi4_agent_tools/probability/operations.js +175 -13
- package/dist/hoi4_agent_tools/probability/source-analysis.js +24 -1
- package/dist/hoi4_agent_tools/reference/error-log.js +359 -0
- package/dist/hoi4_agent_tools/reference/known-mistakes.js +307 -0
- package/dist/hoi4_agent_tools/reference/known-mistakes.json +154 -0
- package/dist/hoi4_agent_tools/reference/mod-index.js +101 -0
- package/dist/hoi4_agent_tools/reference/operations.js +88 -19
- package/dist/hoi4_agent_tools/reference/player-text.js +4 -3
- package/dist/hoi4_agent_tools/reference/script-validation.js +19 -1
- package/dist/hoi4_agent_tools/reference/service.js +6 -0
- package/dist/hoi4_agent_tools/schemas/event.js +78 -19
- package/dist/hoi4_agent_tools/schemas/focus-requests.js +10 -0
- package/dist/hoi4_agent_tools/schemas/lenient-arguments.js +266 -0
- package/dist/hoi4_agent_tools/schemas/map-requests.js +63 -20
- package/dist/hoi4_agent_tools/schemas/map.js +122 -3
- package/dist/hoi4_agent_tools/schemas/mod-tools.js +181 -0
- package/dist/hoi4_agent_tools/schemas/probability-requests.js +119 -9
- package/dist/hoi4_agent_tools/schemas/reference.js +13 -0
- package/dist/hoi4_agent_tools/schemas/scenarios.js +1 -3
- package/dist/hoi4_agent_tools/schemas/script-validation.js +13 -6
- package/dist/hoi4_agent_tools/schemas/technology.js +79 -21
- package/dist/hoi4_agent_tools/technology/job-operations.js +3 -4
- package/dist/hoi4_agent_tools/technology/operations.js +14 -1
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +7 -4
- package/docs/analysis.md +13 -0
- package/docs/configuration.md +281 -0
- package/docs/development.md +24 -2
- package/docs/events.md +27 -3
- package/docs/examples.md +6 -6
- package/docs/focus.md +125 -4
- package/docs/gui.md +98 -17
- package/docs/helper-expansion.md +8 -8
- package/docs/http.md +3 -1
- package/docs/jobs.md +19 -13
- package/docs/map.md +197 -6
- package/docs/mechanics.md +4 -0
- package/docs/probability.md +42 -15
- package/docs/reference.md +71 -4
- package/docs/setup.md +44 -11
- package/docs/technology.md +26 -1
- package/docs/third-party-notices.md +11 -0
- package/docs/tools.md +151 -42
- package/docs/troubleshooting.md +61 -0
- package/examples/clients/codex-config.toml +5 -2
- package/examples/clients/generic.json +2 -2
- package/examples/config/config.json +7 -0
- package/npm-shrinkwrap.json +151 -156
- package/package.json +13 -8
- package/schemas/configuration.schema.json +144 -128
- 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 +55 -0
- 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 +597 -126
- package/schemas/mechanic-test-input.schema.json +1 -1
- package/schemas/mod-index-input.schema.json +51 -0
- package/schemas/operation-result.schema.json +2 -2
- 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 +3 -5
- package/schemas/scenario-test-input.schema.json +3 -5
- 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 +3 -3
|
@@ -32,6 +32,33 @@ export const mapOverlaySchema = z.enum([
|
|
|
32
32
|
'unit-positions',
|
|
33
33
|
'weather-positions',
|
|
34
34
|
]);
|
|
35
|
+
const mapTileSchema = z
|
|
36
|
+
.object({
|
|
37
|
+
x: z.number().int().min(0),
|
|
38
|
+
y: z.number().int().min(0),
|
|
39
|
+
width: z.number().int().min(1).max(2_048),
|
|
40
|
+
height: z.number().int().min(1).max(2_048),
|
|
41
|
+
})
|
|
42
|
+
.strict();
|
|
43
|
+
const mapAreaSchema = z
|
|
44
|
+
.object({
|
|
45
|
+
provinceIds: z.array(z.number().int().nonnegative()).max(256).optional(),
|
|
46
|
+
stateIds: z.array(z.number().int().nonnegative()).max(64).optional(),
|
|
47
|
+
regionIds: z.array(z.number().int().nonnegative()).max(64).optional(),
|
|
48
|
+
padding: z.number().int().min(0).max(256).optional(),
|
|
49
|
+
})
|
|
50
|
+
.strict()
|
|
51
|
+
.refine(({ provinceIds, stateIds, regionIds }) => (provinceIds?.length ?? 0) + (stateIds?.length ?? 0) + (regionIds?.length ?? 0) > 0, 'Map area requires at least one province, state, or region ID');
|
|
52
|
+
/** Fields that select a rendered map view instead of validation and lookups. */
|
|
53
|
+
export const MAP_VIEW_FIELDS = ['layer', 'overlays', 'scale', 'tile', 'area'];
|
|
54
|
+
const MAP_LOOKUP_FIELDS = [
|
|
55
|
+
'provinceIds',
|
|
56
|
+
'stateIds',
|
|
57
|
+
'regionIds',
|
|
58
|
+
'query',
|
|
59
|
+
'coordinates',
|
|
60
|
+
'allocationRequests',
|
|
61
|
+
];
|
|
35
62
|
export const mapInspectRequestSchema = z
|
|
36
63
|
.object({
|
|
37
64
|
workspaceId: workspaceIdSchema,
|
|
@@ -66,33 +93,49 @@ export const mapInspectRequestSchema = z
|
|
|
66
93
|
lookupOnly: z.boolean().default(false),
|
|
67
94
|
includeOverview: z.boolean().default(true),
|
|
68
95
|
allocationRequests: z.array(mapAllocationRequestSchema).max(100).default([]),
|
|
96
|
+
references: z
|
|
97
|
+
.boolean()
|
|
98
|
+
.default(false)
|
|
99
|
+
.describe('Also check events, focuses, decisions, history and AI areas for states, provinces and regions the map lacks, and state tags without a country.'),
|
|
100
|
+
layer: mapLayerSchema
|
|
101
|
+
.optional()
|
|
102
|
+
.describe('Render this base layer instead of validating; combine with overlays, tile or area.'),
|
|
103
|
+
overlays: z.array(mapOverlaySchema).max(12).optional(),
|
|
104
|
+
scale: z.number().int().min(1).max(16).optional(),
|
|
105
|
+
tile: mapTileSchema.optional().describe('Render one top-left bitmap area.'),
|
|
106
|
+
area: mapAreaSchema
|
|
107
|
+
.optional()
|
|
108
|
+
.describe('Render the bounding area of these provinces, states or regions.'),
|
|
69
109
|
})
|
|
70
|
-
.strict()
|
|
110
|
+
.strict()
|
|
111
|
+
.superRefine((value, context) => {
|
|
112
|
+
if (value.tile !== undefined && value.area !== undefined)
|
|
113
|
+
context.addIssue({
|
|
114
|
+
code: 'custom',
|
|
115
|
+
path: ['area'],
|
|
116
|
+
message: 'Choose tile or area, not both',
|
|
117
|
+
});
|
|
118
|
+
if (!MAP_VIEW_FIELDS.some((field) => value[field] !== undefined))
|
|
119
|
+
return;
|
|
120
|
+
for (const field of MAP_LOOKUP_FIELDS) {
|
|
121
|
+
const fieldValue = value[field];
|
|
122
|
+
if (fieldValue !== undefined && !(Array.isArray(fieldValue) && fieldValue.length === 0))
|
|
123
|
+
context.addIssue({
|
|
124
|
+
code: 'custom',
|
|
125
|
+
path: [field],
|
|
126
|
+
message: `${field} is a lookup; request a rendered view (layer, overlays, tile, area, scale) in a separate call`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
/** Internal render request derived from a map inspection that names a view. */
|
|
71
131
|
export const mapRenderRequestSchema = z
|
|
72
132
|
.object({
|
|
73
133
|
workspaceId: workspaceIdSchema,
|
|
74
134
|
layer: mapLayerSchema.optional(),
|
|
75
135
|
overlays: z.array(mapOverlaySchema).max(12).optional(),
|
|
76
136
|
scale: z.number().int().min(1).max(16).optional(),
|
|
77
|
-
tile:
|
|
78
|
-
|
|
79
|
-
x: z.number().int().min(0),
|
|
80
|
-
y: z.number().int().min(0),
|
|
81
|
-
width: z.number().int().min(1).max(2_048),
|
|
82
|
-
height: z.number().int().min(1).max(2_048),
|
|
83
|
-
})
|
|
84
|
-
.strict()
|
|
85
|
-
.optional(),
|
|
86
|
-
area: z
|
|
87
|
-
.object({
|
|
88
|
-
provinceIds: z.array(z.number().int().nonnegative()).max(256).optional(),
|
|
89
|
-
stateIds: z.array(z.number().int().nonnegative()).max(64).optional(),
|
|
90
|
-
regionIds: z.array(z.number().int().nonnegative()).max(64).optional(),
|
|
91
|
-
padding: z.number().int().min(0).max(256).optional(),
|
|
92
|
-
})
|
|
93
|
-
.strict()
|
|
94
|
-
.refine(({ provinceIds, stateIds, regionIds }) => (provinceIds?.length ?? 0) + (stateIds?.length ?? 0) + (regionIds?.length ?? 0) > 0, 'Map area requires at least one province, state, or region ID')
|
|
95
|
-
.optional(),
|
|
137
|
+
tile: mapTileSchema.optional(),
|
|
138
|
+
area: mapAreaSchema.optional(),
|
|
96
139
|
})
|
|
97
140
|
.strict()
|
|
98
141
|
.refine(({ tile, area }) => tile === undefined || area === undefined, 'Choose tile or area, not both');
|
|
@@ -20,7 +20,7 @@ const operationBase = {
|
|
|
20
20
|
};
|
|
21
21
|
const moveStateDistributionSchema = z
|
|
22
22
|
.object({
|
|
23
|
-
stateValues: z.
|
|
23
|
+
stateValues: z.enum(['retain-in-current-states', 'proportional-by-land-pixels']),
|
|
24
24
|
ownership: z.literal('retain-in-current-states'),
|
|
25
25
|
provinceBuildings: z.literal('follow-province'),
|
|
26
26
|
victoryPoints: z.literal('follow-province'),
|
|
@@ -276,6 +276,7 @@ const splitStateOperation = (kind) => z
|
|
|
276
276
|
fileName: z.string().max(255).optional(),
|
|
277
277
|
localisation: stateLocalisationSchema.optional(),
|
|
278
278
|
distribution: splitStateDistributionSchema,
|
|
279
|
+
remapReferences: z.enum(['refuse', 'override']).optional(),
|
|
279
280
|
})
|
|
280
281
|
.strict();
|
|
281
282
|
const createStateOperation = z
|
|
@@ -290,6 +291,7 @@ const createStateOperation = z
|
|
|
290
291
|
fileName: z.string().max(255).optional(),
|
|
291
292
|
localisation: stateLocalisationSchema.optional(),
|
|
292
293
|
distribution: z.union([z.literal('proportional'), splitStateDistributionSchema]).optional(),
|
|
294
|
+
remapReferences: z.enum(['refuse', 'override']).optional(),
|
|
293
295
|
})
|
|
294
296
|
.strict()
|
|
295
297
|
.refine((value) => value.name !== undefined || value.displayName !== undefined, {
|
|
@@ -329,7 +331,7 @@ export const mapOperationSchema = z.discriminatedUnion('kind', [
|
|
|
329
331
|
sourceStateId: stateId,
|
|
330
332
|
targetStateId: stateId,
|
|
331
333
|
provinceIds: z.array(provinceId).min(1).max(100_000),
|
|
332
|
-
distribution: moveStateDistributionSchema,
|
|
334
|
+
distribution: z.union([z.literal('proportional'), moveStateDistributionSchema]).optional(),
|
|
333
335
|
})
|
|
334
336
|
.strict(),
|
|
335
337
|
z
|
|
@@ -408,7 +410,22 @@ export const mapOperationSchema = z.discriminatedUnion('kind', [
|
|
|
408
410
|
kind: z.literal('merge_states'),
|
|
409
411
|
sourceStateIds: z.array(stateId).min(1).max(100_000),
|
|
410
412
|
targetStateId: stateId,
|
|
411
|
-
|
|
413
|
+
// Every merge policy has one allowed value, so the whole object may be omitted.
|
|
414
|
+
distribution: mergeStateDistributionSchema.default({
|
|
415
|
+
stateValues: 'sum-into-target',
|
|
416
|
+
ownership: 'retain-target',
|
|
417
|
+
controller: 'retain-target',
|
|
418
|
+
cores: 'union',
|
|
419
|
+
claims: 'union',
|
|
420
|
+
victoryPoints: 'follow-province',
|
|
421
|
+
provinceBuildings: 'follow-province',
|
|
422
|
+
ports: 'follow-province',
|
|
423
|
+
supplyNodes: 'follow-province',
|
|
424
|
+
railways: 'follow-province',
|
|
425
|
+
positions: 'follow-province',
|
|
426
|
+
strategicRegion: 'require-same',
|
|
427
|
+
}),
|
|
428
|
+
remapReferences: z.enum(['refuse', 'override']).optional(),
|
|
412
429
|
})
|
|
413
430
|
.strict(),
|
|
414
431
|
splitProvinceOperation('split_province'),
|
|
@@ -452,6 +469,108 @@ export const mapOperationSchema = z.discriminatedUnion('kind', [
|
|
|
452
469
|
distribution: z.literal('move-membership'),
|
|
453
470
|
})
|
|
454
471
|
.strict(),
|
|
472
|
+
z
|
|
473
|
+
.object({
|
|
474
|
+
...operationBase,
|
|
475
|
+
kind: z.literal('create_world'),
|
|
476
|
+
width: z.number().int().min(256).max(8_192).multipleOf(256).default(1_024),
|
|
477
|
+
height: z.number().int().min(256).max(4_096).multipleOf(256).default(512),
|
|
478
|
+
seed: z.number().int().min(0).max(2_147_483_647).default(1),
|
|
479
|
+
landCoverage: z.number().min(0.05).max(0.9).default(0.35),
|
|
480
|
+
continents: z.number().int().min(1).max(12).default(3),
|
|
481
|
+
landPolygons: z
|
|
482
|
+
.array(z.array(pointSchema).min(3).max(10_000))
|
|
483
|
+
.max(64)
|
|
484
|
+
.optional()
|
|
485
|
+
.describe('Land outlines in top-left pixel coordinates; omit for generated continents.'),
|
|
486
|
+
landProvincePixels: z.number().int().min(60).max(50_000).default(400),
|
|
487
|
+
seaProvincePixels: z.number().int().min(200).max(200_000).default(2_500),
|
|
488
|
+
provincesPerState: z.number().int().min(1).max(64).default(5),
|
|
489
|
+
landProvincesPerRegion: z.number().int().min(2).max(400).default(25),
|
|
490
|
+
seaProvincesPerRegion: z.number().int().min(1).max(200).default(10),
|
|
491
|
+
countries: z
|
|
492
|
+
.array(z
|
|
493
|
+
.object({
|
|
494
|
+
tag: z.string().regex(/^[A-Z][A-Z0-9]{2}$/u),
|
|
495
|
+
name: z.string().min(1).max(64),
|
|
496
|
+
adjective: z.string().min(1).max(64).optional(),
|
|
497
|
+
color: rgbSchema.optional(),
|
|
498
|
+
share: z.number().min(0.05).max(100).optional(),
|
|
499
|
+
ideology: z.enum(['democratic', 'communism', 'fascism', 'neutrality']).optional(),
|
|
500
|
+
})
|
|
501
|
+
.strict())
|
|
502
|
+
.min(1)
|
|
503
|
+
.max(200),
|
|
504
|
+
continentNames: z.array(z.string().min(1).max(64)).max(12).optional(),
|
|
505
|
+
startDate: z
|
|
506
|
+
.string()
|
|
507
|
+
.regex(/^\d{3,4}\.\d{1,2}\.\d{1,2}$/u)
|
|
508
|
+
.default('1936.1.1'),
|
|
509
|
+
rivers: z.number().int().min(0).max(500).default(8),
|
|
510
|
+
replaceBaseGame: z
|
|
511
|
+
.boolean()
|
|
512
|
+
.default(true)
|
|
513
|
+
.describe('Add replace_path for the base game states, countries, units, strategic regions, supply areas and bookmarks to descriptor.mod.'),
|
|
514
|
+
})
|
|
515
|
+
.strict(),
|
|
516
|
+
z
|
|
517
|
+
.object({
|
|
518
|
+
...operationBase,
|
|
519
|
+
kind: z.literal('remap_map_references'),
|
|
520
|
+
entity: z.enum(['province', 'state', 'strategic-region']),
|
|
521
|
+
mapping: z
|
|
522
|
+
.array(z
|
|
523
|
+
.object({
|
|
524
|
+
from: z.number().int().min(0),
|
|
525
|
+
to: z
|
|
526
|
+
.union([
|
|
527
|
+
z.number().int().min(0),
|
|
528
|
+
z.array(z.number().int().min(0)).min(1).max(16),
|
|
529
|
+
z.null(),
|
|
530
|
+
])
|
|
531
|
+
.describe('One ID replaces; several IDs copy the referencing assignment once per ID; null removes it.'),
|
|
532
|
+
})
|
|
533
|
+
.strict())
|
|
534
|
+
.min(1)
|
|
535
|
+
.max(1_000),
|
|
536
|
+
readOnlySources: z
|
|
537
|
+
.enum(['refuse', 'override'])
|
|
538
|
+
.default('refuse')
|
|
539
|
+
.describe('override copies game or dependency files that need the change into the mod.'),
|
|
540
|
+
pathPrefixes: z.array(z.string().min(1).max(256)).max(32).optional(),
|
|
541
|
+
})
|
|
542
|
+
.strict(),
|
|
543
|
+
z
|
|
544
|
+
.object({
|
|
545
|
+
...operationBase,
|
|
546
|
+
kind: z.literal('regenerate_map_positions'),
|
|
547
|
+
positions: z
|
|
548
|
+
.array(z.enum(['buildings', 'units', 'weather']))
|
|
549
|
+
.min(1)
|
|
550
|
+
.max(3)
|
|
551
|
+
.default(['buildings', 'units', 'weather']),
|
|
552
|
+
stateIds: z.array(stateId).max(10_000).optional(),
|
|
553
|
+
provinceIds: z.array(provinceId).max(100_000).optional(),
|
|
554
|
+
regionIds: z.array(z.number().int().positive()).max(10_000).optional(),
|
|
555
|
+
mode: z
|
|
556
|
+
.enum(['missing', 'replace'])
|
|
557
|
+
.default('missing')
|
|
558
|
+
.describe('missing adds only absent positions; replace rewrites them for the selection.'),
|
|
559
|
+
})
|
|
560
|
+
.strict(),
|
|
561
|
+
z
|
|
562
|
+
.object({
|
|
563
|
+
...operationBase,
|
|
564
|
+
kind: z.literal('rebuild_supply'),
|
|
565
|
+
stateIds: z.array(stateId).max(10_000).optional(),
|
|
566
|
+
mode: z
|
|
567
|
+
.enum(['connect', 'replace'])
|
|
568
|
+
.default('connect')
|
|
569
|
+
.describe('connect adds a supply node to each selected state without one and links unconnected nodes by railway; replace rebuilds the whole network.'),
|
|
570
|
+
railwayLevel: z.number().int().min(1).max(5).default(1),
|
|
571
|
+
capitalRailwayLevel: z.number().int().min(1).max(5).default(3),
|
|
572
|
+
})
|
|
573
|
+
.strict(),
|
|
455
574
|
z
|
|
456
575
|
.object({
|
|
457
576
|
...operationBase,
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { workspaceIdSchema } from './common.js';
|
|
3
|
+
export const errorLogCategorySchema = z.enum([
|
|
4
|
+
'syntax',
|
|
5
|
+
'duplicate',
|
|
6
|
+
'localisation',
|
|
7
|
+
'effect',
|
|
8
|
+
'trigger',
|
|
9
|
+
'scope',
|
|
10
|
+
'graphics',
|
|
11
|
+
'interface',
|
|
12
|
+
'map',
|
|
13
|
+
'missing_reference',
|
|
14
|
+
'other',
|
|
15
|
+
]);
|
|
16
|
+
export const errorLogRequestSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
workspaceId: workspaceIdSchema,
|
|
19
|
+
/** mod: entries located in the mod's own files; unlocated: entries naming no file. */
|
|
20
|
+
scope: z.enum(['all', 'mod', 'unlocated']).default('all'),
|
|
21
|
+
category: errorLogCategorySchema.optional(),
|
|
22
|
+
query: z.string().min(1).max(200).optional(),
|
|
23
|
+
limit: z.number().int().min(1).max(50).default(20),
|
|
24
|
+
offset: z.number().int().min(0).max(1_000_000).default(0),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
export const errorLogDataSchema = z
|
|
28
|
+
.object({
|
|
29
|
+
log: z
|
|
30
|
+
.object({
|
|
31
|
+
path: z.string().max(64),
|
|
32
|
+
modifiedAt: z.string().max(64),
|
|
33
|
+
bytes: z.number().int().min(0),
|
|
34
|
+
truncated: z.boolean(),
|
|
35
|
+
entries: z.number().int().min(0),
|
|
36
|
+
})
|
|
37
|
+
.strict(),
|
|
38
|
+
scope: z.enum(['all', 'mod', 'unlocated']),
|
|
39
|
+
totalEntries: z.number().int().min(0),
|
|
40
|
+
distinctEntries: z.number().int().min(0),
|
|
41
|
+
modEntries: z.number().int().min(0),
|
|
42
|
+
changedSinceLog: z.number().int().min(0),
|
|
43
|
+
categories: z.array(z
|
|
44
|
+
.object({
|
|
45
|
+
category: errorLogCategorySchema,
|
|
46
|
+
entries: z.number().int().min(0),
|
|
47
|
+
distinct: z.number().int().min(0),
|
|
48
|
+
hint: z.string().max(300),
|
|
49
|
+
})
|
|
50
|
+
.strict()),
|
|
51
|
+
/** Messages that differ only in names, numbers and paths, grouped by their shape. */
|
|
52
|
+
patterns: z
|
|
53
|
+
.array(z
|
|
54
|
+
.object({
|
|
55
|
+
pattern: z.string().max(300),
|
|
56
|
+
category: errorLogCategorySchema,
|
|
57
|
+
distinct: z.number().int().min(0),
|
|
58
|
+
entries: z.number().int().min(0),
|
|
59
|
+
example: z.string().max(600),
|
|
60
|
+
})
|
|
61
|
+
.strict())
|
|
62
|
+
.max(8),
|
|
63
|
+
/** Compared with the previous launch's log this server read, when there was one. */
|
|
64
|
+
sinceLastLaunch: z
|
|
65
|
+
.object({
|
|
66
|
+
previousLogAt: z.string().max(64),
|
|
67
|
+
newEntries: z.number().int().min(0),
|
|
68
|
+
persistingEntries: z.number().int().min(0),
|
|
69
|
+
resolvedEntries: z.number().int().min(0),
|
|
70
|
+
resolvedExamples: z
|
|
71
|
+
.array(z
|
|
72
|
+
.object({
|
|
73
|
+
category: errorLogCategorySchema,
|
|
74
|
+
message: z.string().max(300),
|
|
75
|
+
relativePath: z.string().max(1024).optional(),
|
|
76
|
+
})
|
|
77
|
+
.strict())
|
|
78
|
+
.max(5),
|
|
79
|
+
})
|
|
80
|
+
.strict()
|
|
81
|
+
.optional(),
|
|
82
|
+
entries: z
|
|
83
|
+
.array(z
|
|
84
|
+
.object({
|
|
85
|
+
category: errorLogCategorySchema,
|
|
86
|
+
module: z.string().max(128),
|
|
87
|
+
message: z.string().max(600),
|
|
88
|
+
count: z.number().int().min(1),
|
|
89
|
+
firstLogLine: z.number().int().min(1),
|
|
90
|
+
time: z.string().max(16).optional(),
|
|
91
|
+
/** Absent from the previous launch's log. */
|
|
92
|
+
new: z.boolean().optional(),
|
|
93
|
+
/** A reviewed known mistake this message reports, with its documented fix. */
|
|
94
|
+
knownMistake: z
|
|
95
|
+
.object({ id: z.string().max(64), fix: z.string().max(300) })
|
|
96
|
+
.strict()
|
|
97
|
+
.optional(),
|
|
98
|
+
source: z
|
|
99
|
+
.object({
|
|
100
|
+
relativePath: z.string().max(1024),
|
|
101
|
+
line: z.number().int().min(0).optional(),
|
|
102
|
+
layer: z.enum(['mod', 'dependency', 'game', 'unknown']),
|
|
103
|
+
changedSinceLog: z.boolean(),
|
|
104
|
+
})
|
|
105
|
+
.strict()
|
|
106
|
+
.optional(),
|
|
107
|
+
})
|
|
108
|
+
.strict())
|
|
109
|
+
.max(50),
|
|
110
|
+
offset: z.number().int().min(0),
|
|
111
|
+
nextOffset: z.number().int().min(0).optional(),
|
|
112
|
+
})
|
|
113
|
+
.strict();
|
|
114
|
+
export const modIndexRequestSchema = z
|
|
115
|
+
.object({
|
|
116
|
+
workspaceId: workspaceIdSchema,
|
|
117
|
+
mode: z.enum(['overview', 'next_id']).default('overview'),
|
|
118
|
+
/** overview: the mod's own files, or every layer. */
|
|
119
|
+
layer: z.enum(['mod', 'all']).default('mod'),
|
|
120
|
+
/** next_id: the symbol kind whose numbered identifiers are counted. */
|
|
121
|
+
kind: z.enum(['event', 'focus', 'decision', 'idea', 'leader', 'technology']).default('event'),
|
|
122
|
+
prefix: z.string().min(1).max(256).optional(),
|
|
123
|
+
namespace: z.string().min(1).max(256).optional(),
|
|
124
|
+
})
|
|
125
|
+
.strict();
|
|
126
|
+
export const modIndexDataSchema = z
|
|
127
|
+
.object({
|
|
128
|
+
mode: z.enum(['overview', 'next_id']),
|
|
129
|
+
complete: z.boolean(),
|
|
130
|
+
layer: z.enum(['mod', 'all']).optional(),
|
|
131
|
+
/** The mod's descriptor.mod metadata. */
|
|
132
|
+
descriptor: z
|
|
133
|
+
.object({
|
|
134
|
+
name: z.string().max(256).optional(),
|
|
135
|
+
version: z.string().max(256).optional(),
|
|
136
|
+
supportedVersion: z.string().max(256).optional(),
|
|
137
|
+
remoteFileId: z.string().max(256).optional(),
|
|
138
|
+
tags: z.array(z.string().max(256)).max(64),
|
|
139
|
+
dependencies: z.array(z.string().max(256)).max(64),
|
|
140
|
+
replacePaths: z.array(z.string().max(256)).max(64),
|
|
141
|
+
})
|
|
142
|
+
.strict()
|
|
143
|
+
.optional(),
|
|
144
|
+
files: z.number().int().min(0).optional(),
|
|
145
|
+
kinds: z
|
|
146
|
+
.array(z.object({ kind: z.string().max(64), count: z.number().int().min(0) }).strict())
|
|
147
|
+
.optional(),
|
|
148
|
+
folders: z
|
|
149
|
+
.array(z
|
|
150
|
+
.object({
|
|
151
|
+
folder: z.string().max(256),
|
|
152
|
+
files: z.number().int().min(0),
|
|
153
|
+
bytes: z.number().int().min(0),
|
|
154
|
+
})
|
|
155
|
+
.strict())
|
|
156
|
+
.optional(),
|
|
157
|
+
namespaces: z
|
|
158
|
+
.array(z
|
|
159
|
+
.object({
|
|
160
|
+
namespace: z.string().max(256),
|
|
161
|
+
events: z.number().int().min(0),
|
|
162
|
+
lowest: z.number().int().min(0),
|
|
163
|
+
highest: z.number().int().min(0),
|
|
164
|
+
files: z.number().int().min(0),
|
|
165
|
+
})
|
|
166
|
+
.strict())
|
|
167
|
+
.optional(),
|
|
168
|
+
omittedNamespaces: z.number().int().min(0).optional(),
|
|
169
|
+
next: z
|
|
170
|
+
.object({
|
|
171
|
+
kind: z.string().max(64),
|
|
172
|
+
prefix: z.string().max(256),
|
|
173
|
+
used: z.number().int().min(0),
|
|
174
|
+
highest: z.number().int().min(0).optional(),
|
|
175
|
+
nextId: z.string().max(300),
|
|
176
|
+
firstGapId: z.string().max(300).optional(),
|
|
177
|
+
})
|
|
178
|
+
.strict()
|
|
179
|
+
.optional(),
|
|
180
|
+
})
|
|
181
|
+
.strict();
|
|
@@ -5,7 +5,7 @@ function compact(schema, description) {
|
|
|
5
5
|
return z.unknown().describe(description).pipe(schema);
|
|
6
6
|
}
|
|
7
7
|
const nestedSource = compact(probabilitySourceSchema, 'Source selector; snapshotPath binds frozen bytes to path with expectedSourceHash.');
|
|
8
|
-
const nestedScenarios = compact(probabilityScenarioSetSchema, '
|
|
8
|
+
const nestedScenarios = compact(probabilityScenarioSetSchema, 'World-state facts: { id, scenarios: [{ id, actor?, date?, state: { "path": value }, flags?: [names] }] }. Missing ids get defaults; a bare scenario array is accepted.');
|
|
9
9
|
const nestedManifest = compact(customWeightedPoolManifestSchema, 'Declared custom weighted pool and state transitions.');
|
|
10
10
|
const nestedRenderFilter = compact(probabilityRenderFilterSchema, 'Optional scenario, candidate, metric, diagnostic, source, and page filter.');
|
|
11
11
|
const nestedAcceptanceBands = compact(z.array(probabilityAcceptanceBandSchema).max(100_000), 'Optional named metric acceptance bands.');
|
|
@@ -24,26 +24,37 @@ const commonShape = {
|
|
|
24
24
|
outputs,
|
|
25
25
|
refresh: z.boolean().optional(),
|
|
26
26
|
};
|
|
27
|
+
const evaluationShape = {
|
|
28
|
+
scenarioSet: nestedScenarios.optional(),
|
|
29
|
+
horizonDays: z.number().positive().max(1_000_000).optional(),
|
|
30
|
+
metrics: z.array(probabilityMetricSchema).min(1).max(4).optional(),
|
|
31
|
+
acceptanceBands: nestedAcceptanceBands.optional(),
|
|
32
|
+
diagnosticThresholds: nestedDiagnosticThresholds.optional(),
|
|
33
|
+
outputs,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* One call finds the weighted source and, when a single surface matches, evaluates it.
|
|
37
|
+
* Without a scenarioSet the evaluation uses one fact-free scenario, so the result shows base
|
|
38
|
+
* weights and the facts each condition still needs.
|
|
39
|
+
*/
|
|
27
40
|
export const probabilityInspectRequestSchema = z
|
|
28
41
|
.object({
|
|
29
42
|
workspaceId: workspaceIdSchema,
|
|
30
43
|
adapter: probabilityAdapterIdSchema.optional(),
|
|
31
44
|
source: nestedSource.optional(),
|
|
32
45
|
customPoolManifest: nestedManifest.optional(),
|
|
33
|
-
candidatePool,
|
|
46
|
+
candidatePool: candidatePool.describe('Candidate IDs; an event ID selects all of its options, a decision ID finds its file.'),
|
|
47
|
+
evaluate: z
|
|
48
|
+
.boolean()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('Default true: evaluate the matched surface. False returns only the inventory.'),
|
|
51
|
+
...evaluationShape,
|
|
34
52
|
refresh: z.boolean().optional(),
|
|
35
53
|
})
|
|
36
54
|
.strict()
|
|
37
55
|
.superRefine((value, context) => {
|
|
38
56
|
if (value.source !== undefined && value.customPoolManifest !== undefined)
|
|
39
57
|
context.addIssue({ code: 'custom', message: 'Provide either source or customPoolManifest' });
|
|
40
|
-
if (value.adapter !== undefined &&
|
|
41
|
-
value.source === undefined &&
|
|
42
|
-
value.customPoolManifest === undefined)
|
|
43
|
-
context.addIssue({
|
|
44
|
-
code: 'custom',
|
|
45
|
-
message: 'An adapter requires a source; provide a source alone to discover compatible adapters',
|
|
46
|
-
});
|
|
47
58
|
if (value.customPoolManifest !== undefined &&
|
|
48
59
|
value.adapter !== undefined &&
|
|
49
60
|
value.adapter !== 'custom_weighted_pool')
|
|
@@ -52,6 +63,15 @@ export const probabilityInspectRequestSchema = z
|
|
|
52
63
|
path: ['adapter'],
|
|
53
64
|
message: 'customPoolManifest requires adapter custom_weighted_pool',
|
|
54
65
|
});
|
|
66
|
+
if (value.source === undefined &&
|
|
67
|
+
value.customPoolManifest === undefined &&
|
|
68
|
+
(value.candidatePool?.length ?? 0) === 0 &&
|
|
69
|
+
value.adapter !== undefined)
|
|
70
|
+
context.addIssue({
|
|
71
|
+
code: 'custom',
|
|
72
|
+
path: ['source'],
|
|
73
|
+
message: 'Name what to inspect: source.path, source.identifier, or candidatePool IDs such as a decision or event ID',
|
|
74
|
+
});
|
|
55
75
|
});
|
|
56
76
|
export const probabilityEvaluateRequestSchema = z
|
|
57
77
|
.object({
|
|
@@ -166,3 +186,93 @@ export const probabilityRenderRequestSchema = z
|
|
|
166
186
|
filter: nestedRenderFilter.optional(),
|
|
167
187
|
})
|
|
168
188
|
.strict();
|
|
189
|
+
export const probabilityAnalysisKindSchema = z.enum([
|
|
190
|
+
'compare',
|
|
191
|
+
'sweep',
|
|
192
|
+
'simulate',
|
|
193
|
+
'sequence',
|
|
194
|
+
'render',
|
|
195
|
+
]);
|
|
196
|
+
const analysisVariants = {
|
|
197
|
+
compare: probabilityCompareRequestSchema,
|
|
198
|
+
sweep: probabilitySweepRequestSchema,
|
|
199
|
+
simulate: probabilitySimulateRequestSchema,
|
|
200
|
+
sequence: probabilitySequenceRequestSchema,
|
|
201
|
+
render: probabilityRenderRequestSchema,
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Deeper weighted analysis behind one tool. The listed schema is the flat union of every
|
|
205
|
+
* analysis's fields; each request is validated against the schema of its own analysis.
|
|
206
|
+
*/
|
|
207
|
+
export const probabilityAnalyzeRequestSchema = z
|
|
208
|
+
.object({
|
|
209
|
+
workspaceId: workspaceIdSchema,
|
|
210
|
+
analysis: probabilityAnalysisKindSchema.describe('compare: before/after source or manifests; sweep: input ranges; simulate: sampled uncertainty; sequence: declared pool over time; render: images for a retained analysisId.'),
|
|
211
|
+
adapter: probabilityAdapterIdSchema.optional(),
|
|
212
|
+
source: nestedSource.optional(),
|
|
213
|
+
before: nestedSource.optional(),
|
|
214
|
+
after: nestedSource.optional(),
|
|
215
|
+
beforeManifest: nestedManifest.optional(),
|
|
216
|
+
afterManifest: nestedManifest.optional(),
|
|
217
|
+
customPoolManifest: nestedManifest.optional(),
|
|
218
|
+
scenarioSet: nestedScenarios.optional(),
|
|
219
|
+
candidatePool,
|
|
220
|
+
horizonDays: z.number().positive().max(1_000_000).optional(),
|
|
221
|
+
metrics: z.array(probabilityMetricSchema).min(1).max(4).optional(),
|
|
222
|
+
acceptanceBands: nestedAcceptanceBands.optional(),
|
|
223
|
+
diagnosticThresholds: nestedDiagnosticThresholds.optional(),
|
|
224
|
+
outputs,
|
|
225
|
+
sweep: z
|
|
226
|
+
.object({
|
|
227
|
+
paths: z.array(z.string().min(1).max(1024)).min(1).max(32),
|
|
228
|
+
steps: z.number().int().min(2).max(10_000).optional(),
|
|
229
|
+
pairwise: z.boolean().optional(),
|
|
230
|
+
findRankReversals: z.boolean().optional(),
|
|
231
|
+
})
|
|
232
|
+
.strict()
|
|
233
|
+
.optional(),
|
|
234
|
+
samples: z.number().int().min(100).max(10_000_000).optional(),
|
|
235
|
+
seed: z.number().int().min(-2_147_483_648).max(2_147_483_647).optional(),
|
|
236
|
+
confidenceLevel: z.number().min(0.5).max(0.9999).optional(),
|
|
237
|
+
samplingMethod: probabilitySamplingMethodSchema.optional(),
|
|
238
|
+
maxSteps: z.number().int().min(1).max(100_000).optional(),
|
|
239
|
+
analysisId: z.string().min(1).max(256).optional(),
|
|
240
|
+
expectedScenarioHash: z
|
|
241
|
+
.string()
|
|
242
|
+
.regex(/^[a-f0-9]{64}$/u)
|
|
243
|
+
.optional(),
|
|
244
|
+
includeHtml: z.boolean().optional(),
|
|
245
|
+
filter: nestedRenderFilter.optional(),
|
|
246
|
+
refresh: z.boolean().optional(),
|
|
247
|
+
})
|
|
248
|
+
.strict()
|
|
249
|
+
.superRefine((value, context) => {
|
|
250
|
+
const { analysis, ...rest } = value;
|
|
251
|
+
const parsed = analysisVariants[analysis].safeParse(rest);
|
|
252
|
+
if (parsed.success)
|
|
253
|
+
return;
|
|
254
|
+
for (const issue of parsed.error.issues)
|
|
255
|
+
context.addIssue({
|
|
256
|
+
code: 'custom',
|
|
257
|
+
path: issue.path,
|
|
258
|
+
message: issue.code === 'unrecognized_keys'
|
|
259
|
+
? `${issue.message} (not used by analysis ${analysis})`
|
|
260
|
+
: issue.message,
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
/** Split one analyze request into its analysis kind and that analysis's own request. */
|
|
264
|
+
export function splitProbabilityAnalyzeRequest(input) {
|
|
265
|
+
const { analysis, ...rest } = probabilityAnalyzeRequestSchema.parse(input);
|
|
266
|
+
switch (analysis) {
|
|
267
|
+
case 'compare':
|
|
268
|
+
return { analysis, request: probabilityCompareRequestSchema.parse(rest) };
|
|
269
|
+
case 'sweep':
|
|
270
|
+
return { analysis, request: probabilitySweepRequestSchema.parse(rest) };
|
|
271
|
+
case 'simulate':
|
|
272
|
+
return { analysis, request: probabilitySimulateRequestSchema.parse(rest) };
|
|
273
|
+
case 'sequence':
|
|
274
|
+
return { analysis, request: probabilitySequenceRequestSchema.parse(rest) };
|
|
275
|
+
case 'render':
|
|
276
|
+
return { analysis, request: probabilityRenderRequestSchema.parse(rest) };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -171,6 +171,19 @@ export const referenceContextDataSchema = z
|
|
|
171
171
|
missing: z.array(z.string().max(256)).max(32),
|
|
172
172
|
coverage: coverageBySource,
|
|
173
173
|
skipped: count,
|
|
174
|
+
/**
|
|
175
|
+
* Reviewed mistakes for this surface that `hoi4.script_validate` flags in a checked file,
|
|
176
|
+
* each with its fix; the cited wiki heading is in the rule list the server ships.
|
|
177
|
+
*/
|
|
178
|
+
knownMistakes: z
|
|
179
|
+
.array(z
|
|
180
|
+
.object({
|
|
181
|
+
id: z.string().max(64),
|
|
182
|
+
title: z.string().max(120),
|
|
183
|
+
fix: z.string().max(300),
|
|
184
|
+
})
|
|
185
|
+
.strict())
|
|
186
|
+
.max(16),
|
|
174
187
|
})
|
|
175
188
|
.strict();
|
|
176
189
|
export const sourceLookupDataSchema = z
|
|
@@ -167,14 +167,12 @@ const readOnlySuiteTool = z.enum([
|
|
|
167
167
|
'hoi4.tech_inspect',
|
|
168
168
|
'hoi4.tech_render',
|
|
169
169
|
'hoi4.probability_inspect',
|
|
170
|
-
'hoi4.
|
|
171
|
-
'hoi4.probability_render',
|
|
170
|
+
'hoi4.probability_analyze',
|
|
172
171
|
'hoi4.focus_inspect',
|
|
173
172
|
'hoi4.focus_render',
|
|
174
173
|
'hoi4.gui_inspect',
|
|
175
174
|
'hoi4.gui_render',
|
|
176
175
|
'hoi4.map_inspect',
|
|
177
|
-
'hoi4.map_render',
|
|
178
176
|
]);
|
|
179
177
|
const suiteCaseMetadata = {
|
|
180
178
|
sourceSelectors: z.array(manifestItem).max(64).default([]),
|