hoi4-agent-tools 3.0.8 → 3.0.9

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/hoi4_agent_tools/core/condition-evaluator.js +399 -0
  3. package/dist/hoi4_agent_tools/core/condition-model.js +1 -0
  4. package/dist/hoi4_agent_tools/core/condition-schema.js +42 -0
  5. package/dist/hoi4_agent_tools/core/scenario-state.js +97 -0
  6. package/dist/hoi4_agent_tools/core/shared-request-capacity.js +34 -17
  7. package/dist/hoi4_agent_tools/gui/scenario-conditions.js +119 -0
  8. package/dist/hoi4_agent_tools/gui/scenario-generator.js +59 -73
  9. package/dist/hoi4_agent_tools/gui/scenario.js +3 -0
  10. package/dist/hoi4_agent_tools/gui/studio.js +5 -2
  11. package/dist/hoi4_agent_tools/probability/scenario-state.js +2 -95
  12. package/dist/hoi4_agent_tools/probability/trigger-evaluator.js +1 -287
  13. package/dist/hoi4_agent_tools/version.js +1 -1
  14. package/docs/gui.md +3 -1
  15. package/package.json +1 -1
  16. package/schemas/configuration.schema.json +1 -1
  17. package/schemas/continuous-focus-palette.schema.json +1 -1
  18. package/schemas/custom-weighted-pool.schema.json +1 -1
  19. package/schemas/event-feature-manifest.schema.json +1 -1
  20. package/schemas/focus-plan.schema.json +1 -1
  21. package/schemas/focus-planning-sidecar.schema.json +1 -1
  22. package/schemas/gui-animation-source.schema.json +1 -1
  23. package/schemas/gui-helper.schema.json +1 -1
  24. package/schemas/gui-scenario.schema.json +189 -33
  25. package/schemas/map-operation.schema.json +1 -1
  26. package/schemas/operation-result.schema.json +1 -1
  27. package/schemas/probability-analysis-result.schema.json +1 -1
  28. package/schemas/probability-compare-input.schema.json +1 -1
  29. package/schemas/probability-evaluate-input.schema.json +1 -1
  30. package/schemas/probability-inspect-input.schema.json +1 -1
  31. package/schemas/probability-render-input.schema.json +1 -1
  32. package/schemas/probability-scenario-set.schema.json +1 -1
  33. package/schemas/probability-sequence-input.schema.json +1 -1
  34. package/schemas/probability-simulate-input.schema.json +1 -1
  35. package/schemas/probability-sweep-input.schema.json +1 -1
  36. package/server.json +2 -2
@@ -1,287 +1 @@
1
- import { assignments, astPathFor, firstScalar, nodeLocation, } from '../core/source/index.js';
2
- import { resolveScopeContext, rootScopeContext, rootStateValue, scopeIdentity, scopeStateValue, } from './scenario-state.js';
3
- function combineAnd(values) {
4
- if (values.includes('false'))
5
- return 'false';
6
- return values.includes('unresolved') ? 'unresolved' : 'true';
7
- }
8
- function combineOr(values) {
9
- if (values.includes('true'))
10
- return 'true';
11
- return values.includes('unresolved') ? 'unresolved' : 'false';
12
- }
13
- function invert(value) {
14
- return value === 'true' ? 'false' : value === 'false' ? 'true' : 'unresolved';
15
- }
16
- function bool(value) {
17
- if (typeof value === 'boolean')
18
- return value;
19
- if (value === 'yes' || value === 'true')
20
- return true;
21
- if (value === 'no' || value === 'false')
22
- return false;
23
- return undefined;
24
- }
25
- function numeric(value) {
26
- if (typeof value === 'number' && Number.isFinite(value))
27
- return value;
28
- if (typeof value === 'string' && value.trim() !== '' && Number.isFinite(Number(value)))
29
- return Number(value);
30
- return undefined;
31
- }
32
- function compare(left, right, operator) {
33
- if (typeof left === 'number' && typeof right === 'number') {
34
- if (operator === '>')
35
- return left > right;
36
- if (operator === '<')
37
- return left < right;
38
- }
39
- return operator === '!=' ? left !== right : left === right;
40
- }
41
- function unresolved(assignment, candidate, reason) {
42
- return {
43
- state: 'unresolved',
44
- unresolved: [
45
- {
46
- code: 'TRIGGER_UNRESOLVED',
47
- message: reason,
48
- path: assignment.key.value,
49
- candidateId: candidate.id,
50
- ...(candidate.provenance[0] === undefined ? {} : { provenance: candidate.provenance[0] }),
51
- },
52
- ],
53
- };
54
- }
55
- function evaluateCheckVariable(block, scenario, candidate, assignment, scopeContext) {
56
- const variable = firstScalar(block, 'var')?.value;
57
- const target = firstScalar(block, 'value')?.value;
58
- if (variable === undefined || target === undefined)
59
- return unresolved(assignment, candidate, 'check_variable requires declared var and value');
60
- const left = scopeStateValue(scenario, `variable.${variable}`, scopeContext) ??
61
- scopeStateValue(scenario, variable, scopeContext);
62
- const leftNumber = numeric(left);
63
- const rightNumber = numeric(target);
64
- if (leftNumber === undefined || rightNumber === undefined)
65
- return unresolved(assignment, candidate, `Scenario does not declare numeric variable ${variable}`);
66
- const compareName = firstScalar(block, 'compare')?.value ?? 'equals';
67
- const result = compareName === 'greater_than'
68
- ? leftNumber > rightNumber
69
- : compareName === 'greater_than_or_equals'
70
- ? leftNumber >= rightNumber
71
- : compareName === 'less_than'
72
- ? leftNumber < rightNumber
73
- : compareName === 'less_than_or_equals'
74
- ? leftNumber <= rightNumber
75
- : compareName === 'not_equals'
76
- ? leftNumber !== rightNumber
77
- : leftNumber === rightNumber;
78
- return { state: result ? 'true' : 'false', unresolved: [] };
79
- }
80
- function evaluateAssignmentCore(assignment, scenario, candidate, definitions, helperStack, scopeContext) {
81
- const key = assignment.key.value;
82
- if (assignment.value.type === 'block') {
83
- if (key === 'AND')
84
- return evaluateTriggerBlock(assignment.value, scenario, candidate, definitions, helperStack, scopeContext);
85
- if (key === 'OR') {
86
- const children = assignments(assignment.value).map((child) => evaluateAssignment(child, scenario, candidate, definitions, helperStack, scopeContext));
87
- return {
88
- state: combineOr(children.map(({ state }) => state)),
89
- unresolved: children.flatMap(({ unresolved: items }) => items),
90
- helperProvenance: children.flatMap(({ helperProvenance }) => helperProvenance ?? []),
91
- };
92
- }
93
- if (key === 'NOT' || key === 'NOR' || key === 'NAND') {
94
- const children = assignments(assignment.value).map((child) => evaluateAssignment(child, scenario, candidate, definitions, helperStack, scopeContext));
95
- const base = key === 'NAND'
96
- ? combineAnd(children.map(({ state }) => state))
97
- : combineOr(children.map(({ state }) => state));
98
- return {
99
- state: invert(base),
100
- unresolved: children.flatMap(({ unresolved: items }) => items),
101
- helperProvenance: children.flatMap(({ helperProvenance }) => helperProvenance ?? []),
102
- };
103
- }
104
- if (key === 'check_variable')
105
- return evaluateCheckVariable(assignment.value, scenario, candidate, assignment, scopeContext);
106
- if (key === 'is_in_array' || key === 'is_variable_in_array') {
107
- const arrayId = firstScalar(assignment.value, 'array')?.value;
108
- const declaredValue = firstScalar(assignment.value, 'value')?.value;
109
- if (arrayId === undefined || declaredValue === undefined)
110
- return unresolved(assignment, candidate, `${key} requires array and value`);
111
- const array = scopeStateValue(scenario, `array.${arrayId}`, scopeContext) ??
112
- rootStateValue(scenario, `array.${arrayId}`);
113
- if (!Array.isArray(array))
114
- return unresolved(assignment, candidate, `Scenario does not declare array ${arrayId}`);
115
- const compared = ['THIS', 'ROOT', 'PREV'].includes(declaredValue.toUpperCase()) ||
116
- resolveScopeContext(scenario, declaredValue, scopeContext) !== undefined
117
- ? scopeIdentity(resolveScopeContext(scenario, declaredValue, scopeContext) ?? scopeContext)
118
- : (scopeStateValue(scenario, declaredValue, scopeContext) ?? declaredValue);
119
- return { state: array.includes(compared) ? 'true' : 'false', unresolved: [] };
120
- }
121
- const resolvedScope = resolveScopeContext(scenario, key, scopeContext);
122
- if (resolvedScope !== undefined)
123
- return evaluateTriggerBlock(assignment.value, scenario, candidate, definitions, helperStack, resolvedScope);
124
- const helper = definitions.scriptedTriggers.get(key);
125
- if (helper !== undefined) {
126
- if (helperStack.includes(key))
127
- return unresolved(assignment, candidate, `Scripted trigger recursion detected at ${key}`);
128
- if (assignments(assignment.value).length > 0)
129
- return unresolved(assignment, candidate, `Parameterized scripted trigger ${key} is not expanded without declared arguments`);
130
- const helperProvenance = {
131
- path: helper.file.displayPath,
132
- rootKind: helper.file.rootKind,
133
- loadOrder: helper.file.loadOrder,
134
- sourceHash: helper.file.sha256,
135
- location: nodeLocation(helper.document, helper.node, key),
136
- ...(astPathFor(helper.document, helper.node) === undefined
137
- ? {}
138
- : { astPath: astPathFor(helper.document, helper.node) }),
139
- symbol: key,
140
- helperChain: [...helperStack, key],
141
- };
142
- const evaluated = evaluateTriggerBlock(helper.value, scenario, {
143
- ...candidate,
144
- document: helper.document,
145
- provenance: [helperProvenance, ...candidate.provenance],
146
- }, definitions, [...helperStack, key], scopeContext);
147
- return {
148
- ...evaluated,
149
- helperProvenance: [helperProvenance, ...(evaluated.helperProvenance ?? [])],
150
- };
151
- }
152
- const declaredScope = bool(rootStateValue(scenario, `scope.${key}`));
153
- if (declaredScope !== undefined)
154
- return { state: declaredScope ? 'true' : 'false', unresolved: [] };
155
- return unresolved(assignment, candidate, `Scoped or compound trigger ${key} is not declared by the scenario`);
156
- }
157
- const right = assignment.value.value;
158
- if (key === 'always') {
159
- const value = bool(right);
160
- return value === undefined
161
- ? unresolved(assignment, candidate, 'always trigger is not boolean')
162
- : { state: value ? 'true' : 'false', unresolved: [] };
163
- }
164
- if (key === 'has_country_flag' || key === 'has_global_flag' || key === 'has_state_flag') {
165
- const flags = scopeContext.expression === 'ROOT' ? scenario.flags : scopeContext.binding.flags;
166
- return { state: flags?.includes(right) === true ? 'true' : 'false', unresolved: [] };
167
- }
168
- if (key === 'has_event_target') {
169
- const eventTargets = scopeContext.binding.eventTargets ?? scenario.eventTargets;
170
- return {
171
- state: Object.hasOwn(eventTargets ?? {}, right) ? 'true' : 'false',
172
- unresolved: [],
173
- };
174
- }
175
- if (key === 'tag' || key === 'original_tag') {
176
- const actor = scopeContext.expression === 'ROOT'
177
- ? scenario.actor
178
- : (scopeContext.binding.actor ?? scopeContext.binding.id);
179
- if (actor === undefined)
180
- return unresolved(assignment, candidate, `Scenario does not declare actor for ${key}`);
181
- return {
182
- state: compare(actor, right, assignment.operator.text) ? 'true' : 'false',
183
- unresolved: [],
184
- };
185
- }
186
- if (key === 'date') {
187
- if (scenario.date === undefined)
188
- return unresolved(assignment, candidate, 'Scenario does not declare date');
189
- const leftDate = Number(scenario.date.replaceAll('.', ''));
190
- const rightDate = Number(right.replaceAll('.', ''));
191
- if (!Number.isFinite(leftDate) || !Number.isFinite(rightDate))
192
- return unresolved(assignment, candidate, 'Date comparison is not numeric');
193
- return {
194
- state: compare(leftDate, rightDate, assignment.operator.text) ? 'true' : 'false',
195
- unresolved: [],
196
- };
197
- }
198
- const helper = definitions.scriptedTriggers.get(key);
199
- if (helper !== undefined && bool(right) === true) {
200
- if (helperStack.includes(key))
201
- return unresolved(assignment, candidate, `Scripted trigger recursion detected at ${key}`);
202
- const helperProvenance = {
203
- path: helper.file.displayPath,
204
- rootKind: helper.file.rootKind,
205
- loadOrder: helper.file.loadOrder,
206
- sourceHash: helper.file.sha256,
207
- location: nodeLocation(helper.document, helper.node, key),
208
- ...(astPathFor(helper.document, helper.node) === undefined
209
- ? {}
210
- : { astPath: astPathFor(helper.document, helper.node) }),
211
- symbol: key,
212
- helperChain: [...helperStack, key],
213
- };
214
- const evaluated = evaluateTriggerBlock(helper.value, scenario, {
215
- ...candidate,
216
- document: helper.document,
217
- provenance: [helperProvenance, ...candidate.provenance],
218
- }, definitions, [...helperStack, key], scopeContext);
219
- return {
220
- ...evaluated,
221
- helperProvenance: [helperProvenance, ...(evaluated.helperProvenance ?? [])],
222
- };
223
- }
224
- const declared = scopeStateValue(scenario, key, scopeContext);
225
- if (declared === undefined)
226
- return unresolved(assignment, candidate, `Scenario does not declare trigger ${key}`);
227
- const expectedBoolean = bool(right);
228
- const actualBoolean = bool(declared);
229
- if (expectedBoolean !== undefined && actualBoolean !== undefined)
230
- return { state: expectedBoolean === actualBoolean ? 'true' : 'false', unresolved: [] };
231
- const leftNumber = numeric(declared);
232
- const rightNumber = numeric(right);
233
- if (leftNumber !== undefined && rightNumber !== undefined)
234
- return {
235
- state: compare(leftNumber, rightNumber, assignment.operator.text) ? 'true' : 'false',
236
- unresolved: [],
237
- };
238
- if (typeof declared === 'string')
239
- return {
240
- state: compare(declared, right, assignment.operator.text) ? 'true' : 'false',
241
- unresolved: [],
242
- };
243
- return unresolved(assignment, candidate, `Trigger ${key} cannot compare the declared scenario value`);
244
- }
245
- function evaluateAssignment(assignment, scenario, candidate, definitions, helperStack, scopeContext) {
246
- const evaluated = evaluateAssignmentCore(assignment, scenario, candidate, definitions, helperStack, scopeContext);
247
- const source = candidate.provenance[0];
248
- const provenance = candidate.document === undefined || source === undefined
249
- ? source
250
- : {
251
- ...source,
252
- location: nodeLocation(candidate.document, assignment, assignment.key.value),
253
- ...(astPathFor(candidate.document, assignment) === undefined
254
- ? {}
255
- : { astPath: astPathFor(candidate.document, assignment) }),
256
- symbol: assignment.key.value,
257
- };
258
- const expression = assignment.value.type === 'scalar'
259
- ? `${assignment.key.value} ${assignment.operator.text} ${assignment.value.value}`
260
- : assignment.key.value;
261
- return {
262
- ...evaluated,
263
- trace: [
264
- {
265
- operation: 'eligibility',
266
- expression,
267
- applied: evaluated.state,
268
- ...(provenance === undefined ? {} : { provenance }),
269
- },
270
- ...(evaluated.trace ?? []),
271
- ],
272
- };
273
- }
274
- export function evaluateTriggerBlock(block, scenario, candidate, definitions, helperStack = [], scopeContext = rootScopeContext(scenario)) {
275
- const override = scenario.candidateOverrides?.[candidate.id];
276
- if (override !== undefined)
277
- return { state: override ? 'true' : 'false', unresolved: [] };
278
- if (block === undefined)
279
- return { state: 'true', unresolved: [] };
280
- const children = assignments(block).map((assignment) => evaluateAssignment(assignment, scenario, candidate, definitions, helperStack, scopeContext));
281
- return {
282
- state: combineAnd(children.map(({ state }) => state)),
283
- unresolved: children.flatMap(({ unresolved: items }) => items),
284
- helperProvenance: children.flatMap(({ helperProvenance }) => helperProvenance ?? []),
285
- trace: children.flatMap(({ trace }) => trace ?? []),
286
- };
287
- }
1
+ export { evaluateTriggerBlock } from '../core/condition-evaluator.js';
@@ -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 = '3.0.8';
3
+ export const PACKAGE_VERSION = '3.0.9';
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/gui.md CHANGED
@@ -41,7 +41,9 @@ Layout follows Clausewitz orientation and element-origin anchors, including `CEN
41
41
 
42
42
  Scripted-GUI composition follows `parent_scripted_gui` chains even when the linked child window is a top-level sibling in the `.gui` file. Literal `always = yes` and `always = no` visibility and click-enabled blocks are applied automatically. A scenario's `values` object supplies concrete runtime values in one place: variable names resolve numeric text, scripted-localisation names resolve dynamic labels, a progress-bar element name sets its fill value, and element keys ending in `.image`, `.frame`, `.x`, `.y`, `.visible`, or `.enabled` update that visual property. Scripted-GUI property expressions such as `image = "[GetMeterSprite]"` or `x = meter_fill_width` consume matching scenario values automatically. Dynamic text is parsed after scenario and scripted-localisation substitution, so a runtime string containing `£command_power` or `£[GetResourceIcon]` renders the resolved inline HOI4 text icon instead of literal marker text. Dynamic lists instantiate their declared `entry_container` or `country_scope_entry_container`; each scenario row can select the country template with `countryScope`, choose an explicit `entryContainer`, provide `countryTag` and `ideology` for `GetFlag`, and supply row-scoped text, image, frame, position, visibility, and enabled values. Masked country sprites use the actual mask and overlay dimensions, so custom flag sizes are discovered from GFX assets rather than assumed by the renderer.
43
43
 
44
- `hoi4.gui_inspect` and `hoi4.gui_render` generate a plausible seeded runtime scenario by default. The generator follows the complete `parent_scripted_gui` surface, discovers numeric and text tokens in GUI localisation and scripted-GUI properties, chooses real scripted-localisation text and sprite candidates, applies simple source thresholds, fills meters within their declared ranges, populates dynamic lists with coherent rows, selects one overlapping tab pane, keeps active and idle controls mutually exclusive, and leaves values explicitly supplied by the caller unchanged. Normal controls and animation frames remain stable unless variation is explicitly requested. Every explicit `relatedScenarios` entry is rendered beside the generated view.
44
+ `hoi4.gui_inspect` and `hoi4.gui_render` generate a seeded exploratory preview by default. The generator follows the complete `parent_scripted_gui` surface, discovers numeric and text tokens, fills meters within their declared ranges, and populates dynamic lists with coherent rows. Scripted-localisation text and sprite branches use the same structured condition evaluator as probability analysis, including compound conditions, inclusive comparisons, constants, declared scopes, and scripted triggers. An unknown earlier condition leaves the token unresolved instead of guessing a branch. The scenario's `conditionResults` records checked choices and missing inputs. Explicit values remain authoritative; generated inputs are not verified campaign state. Normal controls and animation frames remain stable unless variation is requested. Every explicit `relatedScenarios` entry is rendered beside the generated view.
45
+
46
+ Declare scoped facts with `scopes`, for example `"scopes": { "FROM": { "id": "target", "actor": "FRA", "state": { "support": 10 } } }`. Existing flat values such as `FROM.support` remain usable. Seeded pane and active/idle selection is exploratory layout sampling, not proof that every visibility branch is reachable.
45
47
 
46
48
  Generation is reproducible. Set `generatedScenarios.seed` to any stable string for an exact rerun, use `count` for several plausible versions, or configure numeric bounds, integer or decimal values, list row bounds, text samples, visibility probability, and state variation. Generated numeric values favour ordinary mid-range states, obey literal limits such as `[?capacity]/10`, keep displayed current/limit pairs coherent, and remain inside progress-bar ranges. The generated view is the default. Set `enabled` to `false` when only the supplied placeholder scenario should render, or set `preservePlaceholder` to `true` to include the placeholder beside generated scenarios.
47
49
 
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": "3.0.8",
4
+ "version": "3.0.9",
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/v3.0.8/schemas/configuration.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0.9/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/v3.0.8/schemas/continuous-focus-palette.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0.9/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/v3.0.8/schemas/custom-weighted-pool.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0.9/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/v3.0.8/schemas/event-feature-manifest.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0.9/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/v3.0.8/schemas/focus-plan.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0.9/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/v3.0.8/schemas/focus-planning-sidecar.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0.9/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/v3.0.8/schemas/gui-animation-source.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0.9/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/v3.0.8/schemas/gui-helper.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0.9/schemas/gui-helper.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {