hoi4-agent-tools 3.3.0 → 3.4.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/hoi4_agent_tools/core/condition-evaluator.js +13 -8
  3. package/dist/hoi4_agent_tools/core/helper-expansion.js +3 -0
  4. package/dist/hoi4_agent_tools/event/comparison-selection.js +78 -0
  5. package/dist/hoi4_agent_tools/event/helper-expansion.js +48 -0
  6. package/dist/hoi4_agent_tools/event/operations.js +16 -0
  7. package/dist/hoi4_agent_tools/event/service.js +21 -2
  8. package/dist/hoi4_agent_tools/focus/compact.js +241 -27
  9. package/dist/hoi4_agent_tools/focus/layout.js +119 -8
  10. package/dist/hoi4_agent_tools/focus/render.js +62 -0
  11. package/dist/hoi4_agent_tools/focus/scenario.js +99 -0
  12. package/dist/hoi4_agent_tools/focus/tool-operations.js +30 -4
  13. package/dist/hoi4_agent_tools/gui/assets.js +54 -4
  14. package/dist/hoi4_agent_tools/gui/layout.js +935 -104
  15. package/dist/hoi4_agent_tools/gui/renderer.js +47 -17
  16. package/dist/hoi4_agent_tools/gui/shader.js +515 -0
  17. package/dist/hoi4_agent_tools/gui/source-graph.js +166 -6
  18. package/dist/hoi4_agent_tools/gui/studio.js +245 -19
  19. package/dist/hoi4_agent_tools/gui/tool-operations.js +9 -0
  20. package/dist/hoi4_agent_tools/gui/validators.js +38 -12
  21. package/dist/hoi4_agent_tools/map/operations.js +70 -1
  22. package/dist/hoi4_agent_tools/map/render.js +70 -1
  23. package/dist/hoi4_agent_tools/map/service.js +30 -6
  24. package/dist/hoi4_agent_tools/map/tool-operations.js +67 -1
  25. package/dist/hoi4_agent_tools/mcp/tools/event.js +10 -1
  26. package/dist/hoi4_agent_tools/mcp/tools/focus.js +14 -4
  27. package/dist/hoi4_agent_tools/mcp/tools/gui.js +5 -1
  28. package/dist/hoi4_agent_tools/mcp/tools/map.js +12 -2
  29. package/dist/hoi4_agent_tools/mcp/tools/probability.js +5 -0
  30. package/dist/hoi4_agent_tools/probability/evaluation.js +3 -0
  31. package/dist/hoi4_agent_tools/probability/operations.js +8 -0
  32. package/dist/hoi4_agent_tools/probability/service.js +33 -12
  33. package/dist/hoi4_agent_tools/probability/source-analysis.js +1 -0
  34. package/dist/hoi4_agent_tools/schemas/event.js +8 -0
  35. package/dist/hoi4_agent_tools/schemas/focus-requests.js +40 -1
  36. package/dist/hoi4_agent_tools/schemas/gui-requests.js +8 -0
  37. package/dist/hoi4_agent_tools/schemas/map-requests.js +22 -1
  38. package/dist/hoi4_agent_tools/schemas/technology.js +7 -0
  39. package/dist/hoi4_agent_tools/technology/graph.js +25 -10
  40. package/dist/hoi4_agent_tools/technology/render.js +286 -31
  41. package/dist/hoi4_agent_tools/technology/service.js +55 -8
  42. package/dist/hoi4_agent_tools/technology/source-analysis.js +166 -3
  43. package/dist/hoi4_agent_tools/version.js +1 -1
  44. package/docs/events.md +3 -1
  45. package/docs/focus.md +10 -0
  46. package/docs/gui.md +49 -3
  47. package/docs/map.md +8 -0
  48. package/docs/probability.md +3 -1
  49. package/docs/technology.md +7 -2
  50. package/package.json +1 -1
  51. package/schemas/configuration.schema.json +1 -1
  52. package/schemas/continuous-focus-palette.schema.json +1 -1
  53. package/schemas/custom-weighted-pool.schema.json +1 -1
  54. package/schemas/decision-inspect-input.schema.json +1 -1
  55. package/schemas/event-feature-manifest.schema.json +1 -1
  56. package/schemas/focus-plan.schema.json +1 -1
  57. package/schemas/focus-planning-sidecar.schema.json +1 -1
  58. package/schemas/gui-animation-source.schema.json +1 -1
  59. package/schemas/gui-helper.schema.json +1 -1
  60. package/schemas/gui-scenario.schema.json +1 -1
  61. package/schemas/helper-expansion-request.schema.json +1 -1
  62. package/schemas/helper-expansion-summary.schema.json +1 -1
  63. package/schemas/impact-inspect-input.schema.json +1 -1
  64. package/schemas/map-operation.schema.json +1 -1
  65. package/schemas/mechanic-test-input.schema.json +1 -1
  66. package/schemas/operation-result.schema.json +1 -1
  67. package/schemas/package-check-input.schema.json +1 -1
  68. package/schemas/package-manifest.schema.json +1 -1
  69. package/schemas/probability-analysis-result.schema.json +1 -1
  70. package/schemas/probability-compare-input.schema.json +1 -1
  71. package/schemas/probability-evaluate-input.schema.json +1 -1
  72. package/schemas/probability-inspect-input.schema.json +1 -1
  73. package/schemas/probability-render-input.schema.json +1 -1
  74. package/schemas/probability-scenario-set.schema.json +1 -1
  75. package/schemas/probability-sequence-input.schema.json +1 -1
  76. package/schemas/probability-simulate-input.schema.json +1 -1
  77. package/schemas/probability-sweep-input.schema.json +1 -1
  78. package/schemas/scenario-suite.schema.json +1 -1
  79. package/schemas/scenario-test-input.schema.json +1 -1
  80. package/schemas/shared-scenario.schema.json +1 -1
  81. package/server.json +2 -2
@@ -115,6 +115,12 @@ function resolvedNumber(value, constants) {
115
115
  }
116
116
  return undefined;
117
117
  }
118
+ function resolvedNumericScalar(value, constants) {
119
+ if (!value?.startsWith('@'))
120
+ return value;
121
+ const resolved = resolvedNumber(value, constants);
122
+ return resolved === undefined ? value : String(resolved);
123
+ }
118
124
  function aiMetadata(block) {
119
125
  const ai = childBlocks(block, 'ai_will_do')[0];
120
126
  const weights = childBlocks(block, 'ai_research_weights')[0];
@@ -183,7 +189,7 @@ function addDynamicOrStatic(values, ownerId, document, staticAction, output, kin
183
189
  }
184
190
  function parseTechnologyDefinitions(file, document, fragment) {
185
191
  for (const wrapper of childBlocks(document.root, 'technologies')) {
186
- const constants = constantsIn(wrapper);
192
+ const constants = new Map([...constantsIn(document.root), ...constantsIn(wrapper)]);
187
193
  for (const definition of assignments(wrapper)) {
188
194
  if (definition.key.value.startsWith('@') || definition.value.type !== 'block')
189
195
  continue;
@@ -378,10 +384,12 @@ function parseTechnologyDefinitions(file, document, fragment) {
378
384
  rawSource: document.text.slice(definition.start, definition.end),
379
385
  ...(firstValue(block, 'start_year') === undefined
380
386
  ? {}
381
- : { startYear: firstValue(block, 'start_year') }),
387
+ : { startYear: resolvedNumericScalar(firstValue(block, 'start_year'), constants) }),
382
388
  ...(firstValue(block, 'research_cost') === undefined
383
389
  ? {}
384
- : { researchCost: firstValue(block, 'research_cost') }),
390
+ : {
391
+ researchCost: resolvedNumericScalar(firstValue(block, 'research_cost'), constants),
392
+ }),
385
393
  ...(firstValue(block, 'doctrine_name') === undefined
386
394
  ? {}
387
395
  : { doctrineName: firstValue(block, 'doctrine_name') }),
@@ -612,6 +620,58 @@ function parseTechnologyGridboxes(file, document, fragment, signal) {
612
620
  const absoluteX = current.offsetX + (localX ?? 0);
613
621
  const absoluteY = current.offsetY + (localY ?? 0);
614
622
  const folderId = technologyFolderContainer(current.namedContainers);
623
+ const backgroundSprite = firstValue(block, 'spriteType');
624
+ if (elementType === 'containerwindowtype' && name?.value.endsWith('_folder') === true) {
625
+ const panel = childBlocks(block, 'background')[0];
626
+ const panelSprite = panel === undefined ? undefined : firstValue(panel, 'quadTextureSprite');
627
+ if (panelSprite !== undefined)
628
+ (fragment.backgrounds ??= []).push({
629
+ id: deterministicId('tech-panel', {
630
+ name: name.value,
631
+ path: file.displayPath,
632
+ offset: assignment.start,
633
+ }),
634
+ name: 'folder_panel',
635
+ folderId: name.value,
636
+ sprite: panelSprite,
637
+ position: { x: 0, y: 0 },
638
+ size: {},
639
+ location: nodeLocation(document, assignment, name.value),
640
+ sourcePath: file.displayPath,
641
+ loadOrder: file.loadOrder,
642
+ });
643
+ }
644
+ if (elementType === 'icontype' &&
645
+ name !== undefined &&
646
+ folderId !== undefined &&
647
+ backgroundSprite !== undefined &&
648
+ (name.value.endsWith('_techtree_bg') || backgroundSprite.endsWith('_techtree_bg')) &&
649
+ localX !== undefined &&
650
+ localY !== undefined) {
651
+ const parentSize = childBlocks(current.block, 'size')[0];
652
+ const size = childBlocks(block, 'size')[0] ?? parentSize;
653
+ const width = numericLiteral(size === undefined ? undefined : firstValue(size, 'width'));
654
+ const height = numericLiteral(size === undefined ? undefined : firstValue(size, 'height'));
655
+ const background = {
656
+ id: deterministicId('tech-background', {
657
+ name: name.value,
658
+ path: file.displayPath,
659
+ offset: assignment.start,
660
+ }),
661
+ name: name.value,
662
+ folderId,
663
+ sprite: backgroundSprite,
664
+ position: { x: absoluteX, y: absoluteY },
665
+ size: {
666
+ ...(width === undefined ? {} : { width }),
667
+ ...(height === undefined ? {} : { height }),
668
+ },
669
+ location: nodeLocation(document, assignment, name.value),
670
+ sourcePath: file.displayPath,
671
+ loadOrder: file.loadOrder,
672
+ };
673
+ (fragment.backgrounds ??= []).push(background);
674
+ }
615
675
  if (elementType === 'gridboxtype' && name?.value.endsWith('_tree') === true) {
616
676
  const slotSize = childBlocks(block, 'slotsize')[0];
617
677
  const width = numericLiteral(slotSize === undefined ? undefined : firstValue(slotSize, 'width'));
@@ -659,6 +719,64 @@ function parseTechnologyGridboxes(file, document, fragment, signal) {
659
719
  const size = childBlocks(block, 'size')[0];
660
720
  const width = numericLiteral(size === undefined ? undefined : firstValue(size, 'width'));
661
721
  const height = numericLiteral(size === undefined ? undefined : firstValue(size, 'height'));
722
+ const subTechnologySlots = assignments(block)
723
+ .flatMap((child) => {
724
+ if (child.value.type !== 'block' ||
725
+ child.key.value.toLowerCase() !== 'containerwindowtype')
726
+ return [];
727
+ const slotName = firstValue(child.value, 'name');
728
+ const match = /^sub_technology_slot_(\d+)$/u.exec(slotName ?? '');
729
+ if (match === null)
730
+ return [];
731
+ const slotPosition = childBlocks(child.value, 'position')[0];
732
+ const slotSize = childBlocks(child.value, 'size')[0];
733
+ const x = numericLiteral(slotPosition === undefined ? undefined : firstValue(slotPosition, 'x'));
734
+ const y = numericLiteral(slotPosition === undefined ? undefined : firstValue(slotPosition, 'y'));
735
+ const slotWidth = numericLiteral(slotSize === undefined ? undefined : firstValue(slotSize, 'width'));
736
+ const slotHeight = numericLiteral(slotSize === undefined ? undefined : firstValue(slotSize, 'height'));
737
+ if (x === undefined ||
738
+ y === undefined ||
739
+ slotWidth === undefined ||
740
+ slotHeight === undefined)
741
+ return [];
742
+ const picture = childBlocks(child.value, 'iconType').find((icon) => firstValue(icon, 'name') === 'picture');
743
+ const sprite = picture === undefined ? undefined : firstValue(picture, 'spriteType');
744
+ return [
745
+ {
746
+ index: Number(match[1]),
747
+ position: { x, y },
748
+ size: { width: slotWidth, height: slotHeight },
749
+ ...(sprite === undefined ? {} : { sprite }),
750
+ },
751
+ ];
752
+ })
753
+ .sort((left, right) => left.index - right.index);
754
+ const itemBackground = childBlocks(block, 'background')[0];
755
+ const backgroundSprite = itemBackground === undefined
756
+ ? undefined
757
+ : firstValue(itemBackground, 'quadTextureSprite');
758
+ const icon = childBlocks(block, 'iconType').find((candidate) => firstValue(candidate, 'name') === 'Icon');
759
+ const iconPosition = icon === undefined ? undefined : childBlocks(icon, 'position')[0];
760
+ const iconX = iconPosition === undefined ? undefined : numericLiteral(firstValue(iconPosition, 'x'));
761
+ const iconY = iconPosition === undefined ? undefined : numericLiteral(firstValue(iconPosition, 'y'));
762
+ const designTeam = childBlocks(block, 'iconType').find((candidate) => firstValue(candidate, 'name') === 'can_assign_design_team_icon');
763
+ const designTeamPosition = designTeam === undefined ? undefined : childBlocks(designTeam, 'position')[0];
764
+ const designTeamX = designTeamPosition === undefined
765
+ ? undefined
766
+ : numericLiteral(firstValue(designTeamPosition, 'x'));
767
+ const designTeamY = designTeamPosition === undefined
768
+ ? undefined
769
+ : numericLiteral(firstValue(designTeamPosition, 'y'));
770
+ const designTeamSprite = designTeam === undefined ? undefined : firstValue(designTeam, 'spriteType');
771
+ const nameBox = childBlocks(block, 'instantTextBoxType').find((candidate) => firstValue(candidate, 'name') === 'name');
772
+ const nameBoxPosition = nameBox === undefined ? undefined : childBlocks(nameBox, 'position')[0];
773
+ const nameX = nameBoxPosition === undefined
774
+ ? undefined
775
+ : numericLiteral(firstValue(nameBoxPosition, 'x'));
776
+ const nameY = nameBoxPosition === undefined
777
+ ? undefined
778
+ : numericLiteral(firstValue(nameBoxPosition, 'y'));
779
+ const nameMaxWidth = nameBox === undefined ? undefined : numericLiteral(firstValue(nameBox, 'maxWidth'));
662
780
  const layout = {
663
781
  id: deterministicId('tech-item-layout', {
664
782
  name: name.value,
@@ -688,6 +806,31 @@ function parseTechnologyGridboxes(file, document, fragment, signal) {
688
806
  ? {}
689
807
  : { heightExpression: firstValue(size, 'height') ?? '<missing>' }),
690
808
  },
809
+ ...(backgroundSprite === undefined ? {} : { backgroundSprite }),
810
+ ...(iconX === undefined || iconY === undefined
811
+ ? {}
812
+ : {
813
+ iconPosition: {
814
+ x: iconX,
815
+ y: iconY,
816
+ centered: booleanValue(firstValue(icon, 'centerposition')) === true,
817
+ },
818
+ }),
819
+ ...(designTeamX === undefined ||
820
+ designTeamY === undefined ||
821
+ designTeamSprite === undefined
822
+ ? {}
823
+ : { designTeamIcon: { sprite: designTeamSprite, x: designTeamX, y: designTeamY } }),
824
+ ...(nameX === undefined || nameY === undefined
825
+ ? {}
826
+ : {
827
+ namePosition: {
828
+ x: nameX,
829
+ y: nameY,
830
+ ...(nameMaxWidth === undefined ? {} : { maxWidth: nameMaxWidth }),
831
+ },
832
+ }),
833
+ ...(subTechnologySlots.length === 0 ? {} : { subTechnologySlots }),
691
834
  location: nodeLocation(document, assignment, name.value),
692
835
  sourcePath: file.displayPath,
693
836
  loadOrder: file.loadOrder,
@@ -714,6 +857,26 @@ function parseTechnologyGridboxes(file, document, fragment, signal) {
714
857
  folderId,
715
858
  year,
716
859
  position: { x: absoluteX, y: absoluteY },
860
+ style: {
861
+ ...(firstValue(block, 'font') === undefined
862
+ ? {}
863
+ : { fontName: firstValue(block, 'font') }),
864
+ ...(firstValue(block, 'textColor') === undefined
865
+ ? {}
866
+ : { colour: firstValue(block, 'textColor') }),
867
+ ...(numericLiteral(firstValue(block, 'maxWidth')) === undefined
868
+ ? {}
869
+ : { maxWidth: numericLiteral(firstValue(block, 'maxWidth')) }),
870
+ ...(numericLiteral(firstValue(block, 'maxHeight')) === undefined
871
+ ? {}
872
+ : { maxHeight: numericLiteral(firstValue(block, 'maxHeight')) }),
873
+ ...(firstValue(block, 'format') === undefined
874
+ ? {}
875
+ : { format: firstValue(block, 'format') }),
876
+ ...(firstValue(block, 'Orientation') === undefined
877
+ ? {}
878
+ : { orientation: firstValue(block, 'Orientation') }),
879
+ },
717
880
  location: nodeLocation(document, assignment, name.value),
718
881
  sourcePath: file.displayPath,
719
882
  loadOrder: file.loadOrder,
@@ -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.3.0';
3
+ export const PACKAGE_VERSION = '3.4.0';
4
4
  export const MCP_PROTOCOL_VERSION = LATEST_PROTOCOL_VERSION;
5
5
  export const CONFIG_VERSION = 1;
6
6
  export const TRANSACTION_VERSION = 1;
package/docs/events.md CHANGED
@@ -80,7 +80,9 @@ Example `hoi4.event_render` arguments:
80
80
 
81
81
  ## Compare
82
82
 
83
- `hoi4.event_compare` compares whole workspace event graphs; it does not accept a chain selector. `before` and `after` each accept exactly one graph reference: `{ "revision": "<sha256>" }` or `{ "artifactUri": "hoi4-agent://..." }`. If no reference or overlay is supplied, the tool compares the previous cached revision with a fresh scan of the current source and reports an error when no prior revision is available. Comparisons refresh current source by default so normal agent file edits are detected; set `refresh: false` only when intentionally comparing cached graphs.
83
+ `hoi4.event_compare` compares whole workspace event graphs. `before` and `after` each accept exactly one graph reference: `{ "revision": "<sha256>" }` or `{ "artifactUri": "hoi4-agent://..." }`. If no reference or overlay is supplied, the tool compares the previous cached revision with a fresh scan of the current source and reports an error when no prior revision is available. Comparisons refresh current source by default so normal agent file edits are detected; set `refresh: false` only when intentionally comparing cached graphs.
84
+
85
+ Set `selector` to an event, namespace, file, source location, node, or manifest to view the downstream chain in both revisions. `maxChainNodes` bounds each traversal, defaults to 1,000, and reports `selectionTruncated` when the chain exceeds the boundary. The artifact retains the full comparison and graph hashes, and its `selection` reports selected, omitted, and unattributed changes explicitly. Diagnostics without a chain owner remain unattributed. The rendered view uses the selected changed nodes.
84
86
 
85
87
  Alternatively, compare the current graph with `proposedSources`, an in-memory list of `{ relativePath, source, expectedSourceHash? }` overlays. Set `source` to a string to add or replace source, or to `null` to delete an existing mod source. `after` and `proposedSources` are mutually exclusive. Proposed sources are analyzed without being written. The result reports added, removed, and changed definitions, edges, options, state operations, diagnostics, and route reachability.
86
88
 
package/docs/focus.md CHANGED
@@ -34,6 +34,10 @@ The rewrite measures canvas bounds, same-row spacing, the same curved connector
34
34
 
35
35
  Use the default `layoutMode: "authored"` with a complete plan when deliberately designing coordinates or changing gameplay. Set `layoutMode: "compact"` with a complete plan when creating a large automatically arranged tree. Inspect and render calls remain useful before or after either rewrite mode, but a separate plan is not required for compact cleanup.
36
36
 
37
+ For a bounded layout repair, set `compactFocusIds` to the focus IDs whose positions may change and `pinnedFocusIds` to anchors that must keep their current coordinates. The compact planner preserves the positions and coordinates of every focus outside the selection and leaves gameplay fields untouched. It evaluates candidate layouts against the current tree and reports a constraint error if the selected repair cannot preserve those anchors without worsening connector or spacing defects.
38
+
39
+ Pass `symmetryGroups` with a `centerFocusId` and one or more `{ "leftFocusId": "...", "rightFocusId": "..." }` pairs to `hoi4.focus_rewrite` in compact mode when named branches should align at equal distance and row around a center focus. A pair may contain a pinned or unselected focus; incompatible fixed positions produce a conflict with the specific pair and offsets.
40
+
37
41
  A cleanup should not change gameplay relationships to improve the picture. One `prerequisite` block containing several focus IDs is an OR group; several prerequisite blocks are AND requirements.
38
42
 
39
43
  ## Layout metadata and unsupported script
@@ -46,6 +50,12 @@ If malformed or unsupported script makes a requested change ambiguous, the rewri
46
50
 
47
51
  Inspection and rendering can report missing references, invalid prerequisite structure, duplicate or overlapping positions, insufficient spacing, long or crossing connectors, branch asymmetry, route conflicts, missing localisation or sprites, weak terminal branches, repeated rewards, and missing AI metadata. National-tree inspection also returns the exact `continuous_focus_position` coordinates and linked palette IDs in its compact result, so an agent can identify and move the continuous-focus area without opening the full artifact. Renders are offline review artifacts, not game screenshots.
48
52
 
53
+ For a named tree, pass `scenario: { "completedFocusIds": ["my_route_root"] }` to `hoi4.focus_inspect`. The linked inspection artifact records each focus's prerequisite groups, route locks, exclusive choices, and unresolved runtime fields against that completed set. The inline tree summary counts completed, blocked, unresolved, and structural candidate focuses, plus contradictory completed choices. A structural candidate still requires runtime trigger evaluation before an agent can call it playable.
54
+
55
+ Pass up to 16 `cropFocusIds` to `hoi4.focus_raster` for source-linked problem crops around specific focuses. Each crop uses the same rendered pixels as the full tree; a JSON manifest records the focus ID, crop bounds, image hash, and diagnostics. An unknown focus ID is reported explicitly.
56
+
57
+ The layout JSON reports horizontal, vertical, and Manhattan grid spans for every prerequisite connector in `layout.connectorMeasurements`.
58
+
49
59
  `hoi4.focus_render` is the normal structural view and writes complete HTML, SVG, JSON, and source-map artifacts without decoding every icon or creating a PNG. `hoi4.focus_raster` adds decoded icons and the high-fidelity PNG. National trees with 200 or more focuses raster at half scale by default; pass `reviewScale` from `0.25` through `1` when a different PNG size is needed.
50
60
 
51
61
  The public workflow is regression-tested with 1,024-focus creation, compact rewrites, deterministic repeat runs, inspection, vector rendering, and raster rendering. A separate 1,024-icon test decodes a distinct texture for every focus. Large source diffs use the same deterministic review-artifact path instead of blocking at the quadratic matrix size.
package/docs/gui.md CHANGED
@@ -29,18 +29,50 @@ Rewrite once, then inspect and render the result.
29
29
  5. Fix the existing mod-owned file with targeted patches, a complete supported source replacement, or a structured helper replacement.
30
30
  6. Rewrite once, then inspect and render the result.
31
31
 
32
- Inspection checks missing assets and localisation, invalid sizes, overlap, clipping, overflow, button-label centering, content crossing a background boundary, conflicting click regions, invisible blockers, broken parents or contexts, list-row cuts, state conflicts, trigger/effect gaps, and resolution drift. `hoi4.gui_render` returns a scripted-scenario gallery and JSON delta report alongside the generic state and resolution galleries.
32
+ Inspection checks missing assets and localisation, invalid sizes, overlap, clipping, overflow, button-label centering, content crossing a background boundary, conflicting click regions, invisible blockers, broken parents or contexts, list-row cuts, state conflicts, trigger/effect gaps, and resolution drift. `hoi4.gui_render` returns a scripted-scenario gallery and JSON delta report alongside the generic state and resolution galleries. The scenario matrix records shown and hidden branches, enabled and disabled branches, reasons for hidden or disabled controls, measured label boxes and text overflow, and rendered background boxes with sprite provenance. Use its branch coverage list to identify scenario conditions that still need evidence.
33
+
34
+ For source comparisons, pass `sourceBaseline: { "relativePath": "interface/example.gui", "source": "..." }` to `hoi4.gui_render`. The tool renders that mod-owned `.gui` text and the current source with the same requested scenario, and returns source-baseline and source-comparison PNGs plus a JSON report with source revisions and pixel deltas. `comparisonScenario` remains the separate comparison between two scenarios on the current source.
33
35
 
34
36
  The renderer resolves sprites and fonts through the complete mod, dependency, and installed-game load order. Mod windows therefore render ordinary vanilla sprites, tiled vanilla panels, progress bars, masked shields, button frames, country flags, and localisation icons such as `£command_power` without copying game assets into the mod. Case differences in GFX and font identifiers do not prevent a valid asset from resolving.
35
37
 
36
38
  Bitmap text uses the language-appropriate HOI4 font definition and its real glyph atlas. Header, serif, compact, typewriter, black, inverted, and other declared faces keep their own glyph shapes, native metrics, face colour, border colour, language override, and font-specific `textcolors` palette. The renderer reads BMFont channel roles, separates face and outline coverage, and recognizes both vanilla black-on-white masks and conventional white-on-transparent custom masks. HOI4 localisation colour runs such as `§Y` and `§R` therefore colour the glyph face while retaining its sharp antialiasing and declared border instead of flattening the glyph into a blurred monochrome mask or switching to the tool font. The default language uses the base font rather than an unrelated language override, and the renderer recognizes installed-game atlases whose filename omits a page suffix declared by the matching `.fnt` file.
37
39
 
38
- `uiScale` is the in-game UI scale, not an image zoom. Use `1` for the normal 1920×1080 layout. Bitmap-font glyphs come directly from the real atlas without a sharpening, threshold, opacity-gain, or reconstructed-edge pass. Native-size coverage is unchanged. Enlarged coverage uses restrained Lanczos2 interpolation to avoid blocky pixel replication without running a sharpening filter. Downscaling uses nearest-neighbour sampling. Bitmap placement is snapped to the output pixel grid, and bitmap colours use alpha masks, so native, fractional-scale, local, and VPS renders remain deterministic.
40
+ `uiScale` is the in-game UI scale, not an image zoom. Use `1` for native pixel geometry at any screen resolution. Bitmap-font glyphs come directly from the real atlas without a sharpening, threshold, opacity-gain, or reconstructed-edge pass. Native-size coverage is unchanged. Enlarged coverage uses restrained Lanczos2 interpolation to avoid blocky pixel replication without running a sharpening filter. Downscaling uses nearest-neighbour sampling. Bitmap placement is snapped to the output pixel grid, and bitmap colours use alpha masks, so native, fractional-scale, local, and VPS renders remain deterministic.
39
41
 
40
42
  Layout follows Clausewitz orientation and element-origin anchors, including `CENTER_LEFT`, `CENTER_RIGHT`, `CENTER_UP`, `CENTER_DOWN`, `origo`, `centerposition`, inherited container coordinate origins, local scale, and both `%` and `%%` relative dimensions. Text boxes use native font metrics together with `maxWidth`, `maxHeight`, alignment, wrapping, and `fixedsize` clipping. Cornered tile sprites use fixed nine-slice borders and optional center tiling; progress bars composite their filled and background textures; masked shields composite their background and mask textures.
41
43
 
44
+ A single-percent dimension is a proportional extent; a double-percent dimension names the far edge in the parent and subtracts the element's resolved anchor position.
45
+ For example, a centered root at y=423 with `height=85%%` in a 1440-pixel viewport ends at y=1224 and has height 801, rather than height 1224.
46
+ GUI element type matching is case-insensitive while source spelling and bytes are preserved.
47
+ A zero horizontal or vertical corner border produces the corresponding three-slice composition rather than stretching the entire texture.
48
+ Colour-only progress sprites use their declared size, `color` and `colortwo` without requiring a texture.
49
+ The supported source-backed progress shader selects a colour or texture on each side of its progress threshold; semi-transparent primary pixels do not blend over a second complete texture layer.
50
+ The scene records the selected shader source, hash and entry point.
51
+ Additional shader operations, including animated end-cap sampling, remain explicit unsupported findings rather than accepted pixel-parity evidence.
52
+
53
+ Container scrollbars use referenced or inline `extendedScrollbarType` templates, including their backgrounds, inline track, thumb and end-button artwork.
54
+ Each attachment has independent instance IDs and source provenance even when multiple containers reuse one template.
55
+ The renderer measures direct content bounds and populated grid/list rows, retaining declared list bounds as a minimum, clamps scroll offsets, and applies `autohide_scrollbars`.
56
+ A nested container contributes its declared bounds rather than the overflowing artwork of its descendants.
57
+ Margins inset the scrolling clip without adding layout padding; attached scrollbars anchor to the outer window, and children wholly inside a fixed border remain stationary.
58
+ Clipping is resolved after scroll positioning, so off-screen content can scroll into view and nested scrolling retains the correct ancestor boundaries.
59
+ Visible orthogonal scrollbars reserve their anchored gutters to avoid overlapping end controls.
60
+ Native corner-space behavior still needs matching in-game reference qualification; synthetic non-overlap checks alone do not establish engine parity.
61
+
62
+ Supply `values["container.scrollX"]` and `values["container.scrollY"]` in native UI pixels; these keys also work inside list rows.
63
+ The existing `scrollOffsets` map remains a vertical-offset shorthand, including names with row-instance suffixes.
64
+ `minimum-value` and `maximum-value` scenarios sample content endpoints.
65
+ The scene element's `scroll` record exposes its viewport, measured content dimensions, applied offsets and maximum offsets in output pixels after UI scaling.
66
+ Missing, wrong-axis, duplicate or privately scoped scrollbar references and unclipped/background-less containers produce explicit findings rather than invented templates.
67
+
42
68
  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
69
 
70
+ Native lists can also use an explicit `entryContainer` without fabricated scripted-GUI wiring.
71
+ Each expanded row instance includes its owning list identity, so two lists using the same template cannot overwrite one another's clipping or geometry.
72
+ For native country-flag controls, bind `element_name.countryTag` and `element_name.ideology` in scenario or row values.
73
+ Only valid bounded tag tokens enter asset discovery; an invalid tag is reported, not converted into a different country's identifier or a filesystem path.
74
+ An unrelated global country value does not turn every native icon into a flag.
75
+
44
76
  `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
77
 
46
78
  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.
@@ -104,10 +136,24 @@ GUI rewrites keep complete validation evidence in a linked JSON resource and ret
104
136
 
105
137
  Large inspections, renders, and rewrites send periodic MCP progress heartbeats while a service stage is still running. Clients that request progress can reset their idle request timer from these notifications; there is no 180-second GUI operation limit in the server, and cancellation still follows the request signal.
106
138
 
139
+ ## Source-backed button shading
140
+
141
+ The renderer reads referenced shader source, including legacy `.lua` references whose implementation is a `.shader` file.
142
+ For supported native sprite transforms and straight-alpha blending, a bounded interpreter derives affine RGBA colour operations from the selected normal, hover, pressed or disabled pixel program.
143
+ It supports scalar/vector arithmetic, swizzles, constant interpolation, dot products and clock-dependent constant expressions without executing shader code or arbitrary functions.
144
+ The scene records source identity, effect and entry point; unresolved features, nonlinear sampled-colour operations, additional texture samples and unsupported geometry or blend states remain fidelity findings.
145
+ Supported primary samplers retain point or linear filtering for magnification and minification and use clamped, non-mipmapped texture sampling.
146
+
147
+ Button shading is independent of explicit atlas frames and checkbox checked state.
148
+ Use `values["element.checked"]` for a checkbox's Boolean state, `values["element.pressed"]` for a held button, and `values["element.stateTimeSeconds"]` for elapsed time in the current button state.
149
+ These keys also work in list rows.
150
+ Without a per-element state clock, `animationTimeSeconds` supplies elapsed time; `0` represents entry into the state, not a settled hover.
151
+ Source or scenario `enabled = false` selects disabled appearance and removes the control from click regions.
152
+
107
153
  ## Scenario fidelity
108
154
 
109
155
  The renderer does not run the game engine. Each render includes a fidelity report that separates fields it models from fields it approximates, ignores, cannot resolve, or does not support. Treat that report as part of the review.
110
156
 
111
157
  The generated primary view replaces discovered numeric and text runtime tokens with plausible values. Set `generatedScenarios.preservePlaceholder` to `true`, or disable generation, when the same run should also show unresolved numeric values such as `[?variable|format]` as `[X]` and unresolved text-returning scripted or scoped localisation such as `[GetStatusText]`, `[FROM.GetName]`, and `[?leader_scope.GetName]` as `[dynamic_loc]`. Put `variable` or `GetStatusText` in the supplied scenario's `values` object when an exact value matters; explicit values always take precedence over generated ones and can drive the related meter or state-dependent controls. Supported HOI4 `§` localisation colour controls use the active font's palette and `§!` restores the face colour.
112
158
 
113
- A rewrite stops if malformed or unsupported GUI script makes the requested change ambiguous. Frame-sheet animation, nine-slice tiling, progress composition, masking, and the primary and secondary textures referenced by vanilla GFX definitions render offline. Font selection follows mod/game load order and preview language, accepts Paradox hex and numeric color declarations, and uses the supplied HOI4 bitmap or outline font assets without requiring host font installation. Engine shader programs, hardcoded controls, and dynamic values that are not supplied by a scenario remain fidelity-report entries; an unexecuted shader never hides its resolved base texture.
159
+ A rewrite stops if malformed or unsupported GUI script makes the requested change ambiguous. Frame-sheet animation, nine-slice tiling, progress composition, masking, and the primary and secondary textures referenced by vanilla GFX definitions render offline. Font selection follows mod/game load order and preview language, accepts Paradox hex and numeric color declarations, and uses the supplied HOI4 bitmap or outline font assets without requiring host font installation. Shader operations outside the supported colour subset, hardcoded controls, and dynamic values that are not supplied by a scenario remain fidelity-report entries; an unexecuted shader never hides its resolved base texture.
package/docs/map.md CHANGED
@@ -10,8 +10,14 @@ The linked JSON catalog contains every active province definition, state, and st
10
10
 
11
11
  Use `query` for a compact name or ID result list and `coordinates` for exact pixel or map-coordinate lookup. Use `provinceIds`, `stateIds`, and `regionIds` for focused records. Up to 32 selected provinces can also produce exact geometry as maximal `[y, startX, endXExclusive]` row runs.
12
12
 
13
+ Set `lookupOnly: true` for coordinate, query, and selected-ID lookups without full validation, the complete catalog artifact, overview rendering, or province geometry export. The lookup artifact contains only the requested matches and source revision. Use full inspection for allocation previews or province row runs.
14
+
13
15
  `hoi4.map_render` creates the same complete catalog and navigator with a chosen base layer and overlays. Base layers include province, state, strategic region, terrain, continent, owner, controller, cores, claims, and coast. Overlays include coastlines, ports, victory points, resources, state and province buildings, supply nodes, railways, adjacencies, and building, unit, and weather positions.
14
16
 
17
+ Pass `tile: { x, y, width, height }` to `hoi4.map_render` for a reusable bounded PNG, JSON, and HTML view of one top-left bitmap area. The tile preserves the selected base layer and overlays, and its JSON records raster dimensions and province IDs in the area. Identical source revisions and render settings produce identical artifact hashes.
18
+
19
+ Pass `area: { provinceIds, stateIds, regionIds, padding }` to render the exact bounding area of those map entities with optional pixel padding. The result reports the resolved tile coordinates. An unknown ID or an area larger than 2,048 pixels in either direction is explicit, so a narrower selection can be requested.
20
+
15
21
  ## Create states
16
22
 
17
23
  The compact `create_state` form needs selected provinces and a display name:
@@ -67,6 +73,8 @@ Use `renumber_map_entity` for `province`, `state`, or `strategic-region` IDs:
67
73
 
68
74
  If the destination exists, the default behavior swaps the two IDs. Province swaps update definitions, state and region membership, victory points, province buildings, adjacency, supply nodes, railways, unit positions, building sea references, and standard province and victory-point localisation keys. State swaps update state records, standard state localisation keys, and building-position state references. Strategic-region swaps update region records, standard localisation keys, and weather-position references. Province and strategic-region operations preserve their required contiguous ID sets.
69
75
 
76
+ Renumbering also inspects active event, focus, decision, scripted helper, on-action, idea, character, and country-history source for typed scalar map fields. It rewrites only parsed values in `province`, `province_id`, `start_province`, `target_province`, `state`, `state_id`, `start_state`, `target_state`, `owns_state`, `controls_state`, `transfer_state`, `transfer_state_to`, `strategic_region`, and `strategic_region_id`. Other numbers, comments, and identifiers remain untouched. A matching reference in a read-only game or dependency source blocks the operation with its path and field. A script that cannot be safely parsed also blocks the operation.
77
+
70
78
  Set `collision` to `reject` to require an unused destination. Set `renameLocalisation` to `false` only when custom localisation-key handling is intentional.
71
79
 
72
80
  ## Other map changes
@@ -16,7 +16,9 @@ Use them for event timing and option weights, decision and mission scores, focus
16
16
 
17
17
  All seven tools are read-only. Proposed source is parsed in memory and never written. When an installed game root is configured, evaluation fails closed unless `launcher-settings.json` identifies the supported HOI4 build and checksum. Results otherwise state that they target the adapter version without claiming local-game verification.
18
18
 
19
- Pass a source without an adapter when the surface type is not yet known. Inspection returns every compatible adapter, candidate counts, and example candidate IDs. If a requested adapter, identifier, or candidate pool does not match the source, inspection returns the same discovery result with an explanation and suggested adapter instead of `PROBABILITY_SURFACE_EMPTY`.
19
+ Pass a source without an adapter when the surface type is not yet known. Inspection returns every compatible adapter, candidate counts, and example candidate IDs; its discovery artifact includes every candidate ID and source location. If a requested adapter, identifier, or candidate pool does not match the source, inspection returns the same discovery result with an explanation and suggested adapter instead of `PROBABILITY_SURFACE_EMPTY`.
20
+
21
+ For a selected source, `hoi4.probability_inspect` includes missing source-condition fields in compact `requiredInputPaths`, adapter-wide completeness flags in `adapterRequiredInputPaths`, and the full candidate catalog, source provenance, and per-candidate requirements in its JSON resource. Unsupported source constructs remain unresolved evidence rather than being presented as scenario inputs. Evaluation reports eligible, excluded, and unresolved candidate counts inline; the resource preserves each candidate's exact eligibility and unresolved reasons for every scenario.
20
22
 
21
23
  Evaluation, sweep, simulation, and source comparison also accept an omitted adapter. When a source selector identifies one unambiguous weighted surface, the analyzer selects that source-backed adapter automatically. If a caller supplies a mismatched adapter or `custom_weighted_pool` for a source-backed request, the analyzer corrects it when the source has exactly one match. `customPoolManifest`, `beforeManifest`, and `afterManifest` use the custom-pool adapter automatically; `PROBABILITY_SURFACE_EMPTY` is reserved for sources with no weighted surface, while genuinely multi-adapter sources ask the caller to narrow by identifier or line.
22
24
 
@@ -47,15 +47,18 @@ Impact mode takes a subject with `kind` set to `technology`, `category`, `folder
47
47
  Findings are separated into confirmed errors, probable defects, design warnings, and unresolved analysis. Roots, grant-only technologies, early dates, zero AI weights, repeated effects, and routing nodes are evaluated in context instead of being treated as automatic defects. The viewer keeps its authoritative graph in memory while the linked scan artifact remains a bounded summary; use its revision with the focused inspect modes or a render resource to inspect the relevant records.
48
48
 
49
49
  Ordinary technology inspection uses the materialized graph and its explicit helper-depth and projection limits.
50
+ Workspaces with a game root, more than 1,000 scanned files, or more than 4,096 helper calls retain direct references and expose deferred helper expansion as incomplete coverage; `helper_expansion` pages provide bounded paths for those workspaces.
50
51
  A truncated helper expansion reports incomplete coverage.
51
52
  For a large helper closure, [bounded helper expansion](helper-expansion.md) uses the structural call inventory and streams source-linked paths without first materializing the full projection.
52
53
  It preserves separate conditional call sites and identifies source, cycle, and depth boundaries without simulating trigger outcomes.
53
54
 
54
55
  ## Render
55
56
 
56
- `hoi4.tech_render` returns authoritative JSON with deterministic SVG and PNG resources; set `includeHtml` when a bundled static report is useful. Folder renders read the actual `techtree_<folder>_item` and `techtree_<folder>_small_item` geometry, choose the small item for technologies that do not unlock equipment or explicitly force it, and include the source GUI's horizontal or vertical year guide. Available views are `summary`, `folder`, `dependencies`, `technology`, `doctrine`, `exclusive`, `memberships`, `bonuses`, `grants`, `unlocks`, `metadata`, `assets`, and `unresolved`.
57
+ `hoi4.tech_render` returns authoritative JSON with deterministic SVG and PNG resources; set `includeHtml` when a bundled static report is useful. Folder renders read the actual `techtree_<folder>_item` and `techtree_<folder>_small_item` geometry, choose the small item for technologies that do not unlock equipment or explicitly force it, and place the source GUI's year labels. Available views are `summary`, `folder`, `dependencies`, `technology`, `doctrine`, `exclusive`, `memberships`, `bonuses`, `grants`, `unlocks`, `metadata`, `assets`, and `unresolved`.
57
58
 
58
- Folder renders use the actual folder assignments, gridbox geometry, technology coordinates, and HOI4 item size found in source. Technologies with `force_use_small_tech_layout = yes` render as compact icon items; normal technologies render as large items with their icon and label. Sprite declarations and texture files resolve through the active load order across the mod, configured dependencies, and the detected or configured game installation, so vanilla icons used by a mod appear in SVG, PNG, and HTML artifacts. The authoritative JSON and SVG expose `layoutSize`, rendered-icon coverage, and unresolved sprite names without embedding image data in JSON. Dependency and other semantic views are explicitly labelled as generated analysis layouts.
59
+ Folder renders use folder assignments, gridbox geometry, technology coordinates, and item dimensions found in source. File-local `@` constants in technology coordinates are resolved before placement. A technology with multiple prerequisite roots can use its own declared gridbox. The item template supplies its unavailable, available, or researched skin, icon center, name position, and design-team icon position when the corresponding sprites resolve. `sub_technology_slot_<index>` geometry positions each declared subtechnology icon; a missing slot stays visible as unresolved placement metadata. The folder's tiled panel sprite fills the canvas, and source `*_techtree_bg` sprites render behind technology nodes at their declared GUI positions; when the GUI omits a sprite size, the decoded dimensions supply it. Year labels retain their source font, box dimensions, alignment, orientation, and colour declarations in the graph. Sprite declarations and texture files resolve through the active load order across the mod, configured dependencies, and the detected or configured game installation. The authoritative JSON and SVG expose `layoutSize`, rendered sprite coverage, and unresolved sprite names without embedding image data in JSON. `sourceAccurate` describes source pixel placement only; it is not a percentage of visual agreement with a game screenshot. The game may change the visibility of design-team icons and status corners at runtime. Dependency and other semantic views are labelled as generated analysis layouts.
60
+
61
+ A `small` 72-pixel item can still display its source `GFX_*_medium` artwork at full card size; the layout name does not select smaller icon art.
59
62
 
60
63
  Example folder render:
61
64
 
@@ -68,6 +71,8 @@ Example folder render:
68
71
  }
69
72
  ```
70
73
 
74
+ Pass `scenario: { "year": 1936, "researchedTechnologyIds": ["infantry_weapons"] }` to `hoi4.tech_render` to display known researched technologies, exclusive conflicts, and missing source prerequisites. Folder renders select the corresponding item skin when its sprite is available. The JSON lists the missing prerequisite IDs and years ahead of the scenario date for each displayed technology. A `structural_candidate` status means the supplied research set has no known structural blocker; country conditions and other dynamic requirements remain unresolved. Unknown researched IDs are rejected.
75
+
71
76
  Large dependency requests return a bounded overview plus focused folder resources and a coverage manifest. The graph itself is not truncated: use the manifest and linked `hoi4-agent://` resources to inspect the relevant folder or branch without loading the entire system into the prompt.
72
77
 
73
78
  ## Compare
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.3.0",
4
+ "version": "3.4.0",
5
5
  "description": "Analyze HOI4 events, decisions, impact, tech, AI, 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.3.0/schemas/configuration.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/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.3.0/schemas/continuous-focus-palette.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/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.3.0/schemas/custom-weighted-pool.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/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.3.0/schemas/decision-inspect-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/decision-inspect-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.3.0/schemas/event-feature-manifest.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/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.3.0/schemas/focus-plan.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/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.3.0/schemas/focus-planning-sidecar.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/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.3.0/schemas/gui-animation-source.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/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.3.0/schemas/gui-helper.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/gui-helper.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.3.0/schemas/gui-scenario.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/gui-scenario.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.3.0/schemas/helper-expansion-request.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/helper-expansion-request.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.3.0/schemas/helper-expansion-summary.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/helper-expansion-summary.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.3.0/schemas/impact-inspect-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/impact-inspect-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.3.0/schemas/map-operation.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/map-operation.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "oneOf": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.3.0/schemas/mechanic-test-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/mechanic-test-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.3.0/schemas/operation-result.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/operation-result.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.3.0/schemas/package-check-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/package-check-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.3.0/schemas/package-manifest.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.0/schemas/package-manifest.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {