hoi4-agent-tools 2.5.0 → 2.5.1

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.5.1 - 2026-08-13
4
+
5
+ - Published artifact contents and provenance manifests atomically across independent MCP processes, preventing event, probability, GUI, and other concurrent inspections from observing partially written manifests.
6
+ - Recovered clearly interrupted zero-filled or truncated provenance manifests during matching writes and storage pruning while continuing to reject arbitrary malformed or tampered manifests.
7
+ - Tolerated temporary-file disappearance during concurrent storage scans without weakening artifact integrity validation.
8
+ - Added cross-process publication, interrupted-write recovery, and fail-closed integrity regressions, then verified event inspection, probability inspection, and artifact-resource workflows against the repaired store.
9
+
3
10
  ## 2.5.0 - 2026-08-09
4
11
 
5
12
  - Added exact continuous-focus coordinates and linked palette IDs to compact national focus inspection results.
@@ -1,5 +1,5 @@
1
- import { createHash } from 'node:crypto';
2
- import { lstat, mkdir, open, readFile, readdir, stat, unlink, writeFile } from 'node:fs/promises';
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { link, lstat, mkdir, open, readFile, readdir, stat, unlink } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import { z } from 'zod/v4';
5
5
  import { canonicalJson, compareCodeUnits, sha256Bytes } from './canonical.js';
@@ -159,6 +159,8 @@ const artifactManifestMaxBytes = 1_048_576;
159
159
  const artifactLogicalBatchMaxBytes = 536_870_912;
160
160
  const artifactHashYieldBytes = 16_777_216;
161
161
  const artifactPreflightYieldEntries = 256;
162
+ const interruptedManifestMinimumAgeMs = 1_000;
163
+ const artifactTemporaryPrefix = '.artifact-tmp-';
162
164
  const ownedArtifactContent = Symbol('ownedArtifactContent');
163
165
  const precomputedArtifactSha256 = Symbol('precomputedArtifactSha256');
164
166
  export const chunkedArtifactIndexMimeType = 'application/json';
@@ -320,11 +322,22 @@ async function artifactUsage(root, signal) {
320
322
  if (child.isSymbolicLink()) {
321
323
  throw new ServiceError('ARTIFACT_STORAGE_UNSAFE', 'Artifact storage contains a symbolic link or junction');
322
324
  }
325
+ if (child.isFile() && child.name.startsWith(artifactTemporaryPrefix))
326
+ continue;
323
327
  const candidate = await containedGeneratedPath(root, path.relative(root, path.join(directory, child.name)));
324
328
  if (child.isDirectory())
325
329
  await walk(candidate);
326
330
  else if (child.isFile()) {
327
- bytes += (await stat(candidate)).size;
331
+ let size;
332
+ try {
333
+ size = (await stat(candidate)).size;
334
+ }
335
+ catch (error) {
336
+ if (error.code === 'ENOENT')
337
+ continue;
338
+ throw error;
339
+ }
340
+ bytes += size;
328
341
  if (child.name.endsWith('.manifest.json'))
329
342
  entries += 1;
330
343
  if (!Number.isSafeInteger(bytes)) {
@@ -375,6 +388,101 @@ async function readArtifactManifest(filePath, signal) {
375
388
  throw new ServiceError('ARTIFACT_MANIFEST_INVALID', 'Artifact provenance manifest is invalid');
376
389
  }
377
390
  }
391
+ async function readBoundedManifestBytes(filePath, signal) {
392
+ signal?.throwIfAborted();
393
+ let metadata;
394
+ try {
395
+ metadata = await lstat(filePath);
396
+ }
397
+ catch {
398
+ throw new ServiceError('ARTIFACT_NOT_FOUND', 'Artifact provenance manifest is unavailable');
399
+ }
400
+ if (metadata.isSymbolicLink() || !metadata.isFile()) {
401
+ throw new ServiceError('ARTIFACT_MANIFEST_INVALID', 'Artifact provenance manifest is invalid');
402
+ }
403
+ if (metadata.size > artifactManifestMaxBytes) {
404
+ throw new ServiceError('ARTIFACT_MANIFEST_LIMIT', 'Artifact provenance manifest exceeds its fixed byte limit');
405
+ }
406
+ try {
407
+ const bytes = signal === undefined ? await readFile(filePath) : await readFile(filePath, { signal });
408
+ return { bytes, metadata };
409
+ }
410
+ catch (error) {
411
+ if (error.name === 'AbortError')
412
+ throw error;
413
+ throw new ServiceError('ARTIFACT_NOT_FOUND', 'Artifact provenance manifest is unavailable');
414
+ }
415
+ }
416
+ function zeroFilled(bytes) {
417
+ return bytes.length === 0 || bytes.every((value) => value === 0);
418
+ }
419
+ async function interruptedManifestState(filePath, expectedBytes, signal) {
420
+ const { bytes, metadata } = await readBoundedManifestBytes(filePath, signal);
421
+ const expected = expectedBytes === undefined ? undefined : Buffer.from(expectedBytes, 'utf8');
422
+ const expectedPrefix = expected !== undefined &&
423
+ bytes.length < expected.length &&
424
+ expected.subarray(0, bytes.length).equals(bytes);
425
+ const staleZeroFill = zeroFilled(bytes) && Date.now() - Number(metadata.mtimeMs) >= interruptedManifestMinimumAgeMs;
426
+ return { interrupted: expectedPrefix || staleZeroFill, size: Number(metadata.size) };
427
+ }
428
+ async function existingManifestMatches(filePath, expectedBytes, signal) {
429
+ if (!(await fileExists(filePath)))
430
+ return false;
431
+ try {
432
+ const manifest = await readArtifactManifest(filePath, signal);
433
+ if (`${canonicalJson(manifest)}\n` !== expectedBytes) {
434
+ throw new ServiceError('ARTIFACT_MANIFEST_INTEGRITY_FAILED', 'Immutable artifact provenance manifest contains different bytes');
435
+ }
436
+ return true;
437
+ }
438
+ catch (error) {
439
+ if (error instanceof ServiceError && error.code === 'ARTIFACT_NOT_FOUND')
440
+ return false;
441
+ if (!(error instanceof ServiceError) || error.code !== 'ARTIFACT_MANIFEST_INVALID')
442
+ throw error;
443
+ const state = await interruptedManifestState(filePath, expectedBytes, signal);
444
+ if (!state.interrupted)
445
+ throw error;
446
+ try {
447
+ await unlink(filePath);
448
+ }
449
+ catch (unlinkError) {
450
+ if (unlinkError.code !== 'ENOENT')
451
+ throw unlinkError;
452
+ }
453
+ return false;
454
+ }
455
+ }
456
+ async function publishExclusiveFile(target, bytes, signal) {
457
+ signal?.throwIfAborted();
458
+ const temporary = path.join(path.dirname(target), `${artifactTemporaryPrefix}${process.pid}-${randomUUID().replaceAll('-', '')}`);
459
+ let handle;
460
+ try {
461
+ handle = await open(temporary, 'wx');
462
+ await handle.writeFile(bytes, signal === undefined ? undefined : { signal });
463
+ await handle.sync();
464
+ await handle.close();
465
+ handle = undefined;
466
+ signal?.throwIfAborted();
467
+ try {
468
+ await link(temporary, target);
469
+ return true;
470
+ }
471
+ catch (error) {
472
+ if (error.code === 'EEXIST')
473
+ return false;
474
+ throw error;
475
+ }
476
+ }
477
+ finally {
478
+ if (handle !== undefined)
479
+ await handle.close().catch(() => undefined);
480
+ await unlink(temporary).catch((error) => {
481
+ if (error.code !== 'ENOENT')
482
+ throw error;
483
+ });
484
+ }
485
+ }
378
486
  async function readVerifiedArtifactRange(filePath, expectedSha256, range, verifiedIdentity, signal) {
379
487
  signal?.throwIfAborted();
380
488
  if (range !== undefined &&
@@ -495,8 +603,47 @@ export class ArtifactStore {
495
603
  }
496
604
  if (!child.isFile() || !child.name.endsWith('.manifest.json'))
497
605
  continue;
498
- const metadata = await lstat(candidate);
499
- const manifest = await readArtifactManifest(candidate, signal);
606
+ let metadata;
607
+ try {
608
+ metadata = await lstat(candidate);
609
+ }
610
+ catch (error) {
611
+ if (error.code === 'ENOENT')
612
+ continue;
613
+ throw error;
614
+ }
615
+ let manifest;
616
+ try {
617
+ manifest = await readArtifactManifest(candidate, signal);
618
+ }
619
+ catch (error) {
620
+ if (error instanceof ServiceError && error.code === 'ARTIFACT_NOT_FOUND')
621
+ continue;
622
+ if (!(error instanceof ServiceError) || error.code !== 'ARTIFACT_MANIFEST_INVALID')
623
+ throw error;
624
+ let interrupted;
625
+ try {
626
+ interrupted = await interruptedManifestState(candidate, undefined, signal);
627
+ }
628
+ catch (interruptedError) {
629
+ if (interruptedError instanceof ServiceError &&
630
+ interruptedError.code === 'ARTIFACT_NOT_FOUND')
631
+ continue;
632
+ throw interruptedError;
633
+ }
634
+ if (!interrupted.interrupted)
635
+ throw error;
636
+ try {
637
+ await unlink(candidate);
638
+ }
639
+ catch (unlinkError) {
640
+ if (unlinkError.code !== 'ENOENT')
641
+ throw unlinkError;
642
+ }
643
+ usage.entries = Math.max(0, usage.entries - 1);
644
+ usage.bytes = Math.max(0, usage.bytes - interrupted.size);
645
+ continue;
646
+ }
500
647
  if (child.name !== `${manifest.name}.${manifest.provenanceHash}.manifest.json`) {
501
648
  throw new ServiceError('ARTIFACT_MANIFEST_INTEGRITY_FAILED', 'Artifact provenance manifest filename does not match its contents');
502
649
  }
@@ -993,13 +1140,8 @@ export class ArtifactStore {
993
1140
  throw new ServiceError('ARTIFACT_MANIFEST_INTEGRITY_FAILED', 'Artifact batch maps different provenance to the same immutable manifest');
994
1141
  }
995
1142
  if (duplicateManifest === undefined) {
996
- const exists = await fileExists(artifact.manifestPath);
1143
+ const exists = await existingManifestMatches(artifact.manifestPath, artifact.manifestBytes, signal);
997
1144
  signal?.throwIfAborted();
998
- if (exists &&
999
- `${canonicalJson(await readArtifactManifest(artifact.manifestPath, signal))}\n` !==
1000
- artifact.manifestBytes) {
1001
- throw new ServiceError('ARTIFACT_MANIFEST_INTEGRITY_FAILED', 'Immutable artifact provenance manifest contains different bytes');
1002
- }
1003
1145
  uniqueManifests.set(artifact.manifestPath, { ...artifact, exists });
1004
1146
  }
1005
1147
  }
@@ -1020,18 +1162,19 @@ export class ArtifactStore {
1020
1162
  continue;
1021
1163
  await mkdir(artifact.directory, { recursive: true });
1022
1164
  signal?.throwIfAborted();
1023
- try {
1024
- await writeFile(artifact.target, artifact.bytes, {
1025
- flag: 'wx',
1026
- ...(signal === undefined ? {} : { signal }),
1027
- });
1165
+ const created = await publishExclusiveFile(artifact.target, artifact.bytes, signal);
1166
+ if (created) {
1028
1167
  createdTargets.push(artifact.target);
1029
1168
  }
1030
- catch (error) {
1031
- if (error.code !== 'EEXIST') {
1032
- createdTargets.push(artifact.target);
1169
+ else {
1170
+ try {
1171
+ await readVerifiedArtifactRange(artifact.target, artifact.artifact.sha256, { offset: 0, length: 1 }, undefined, signal);
1172
+ }
1173
+ catch (error) {
1174
+ if (error.name === 'AbortError')
1175
+ throw error;
1176
+ throw new ServiceError('ARTIFACT_HASH_COLLISION', 'Artifact path contains different bytes');
1033
1177
  }
1034
- throw error;
1035
1178
  }
1036
1179
  }
1037
1180
  for (const artifact of uniqueManifests.values()) {
@@ -1040,18 +1183,12 @@ export class ArtifactStore {
1040
1183
  continue;
1041
1184
  await mkdir(artifact.directory, { recursive: true });
1042
1185
  signal?.throwIfAborted();
1043
- try {
1044
- await writeFile(artifact.manifestPath, artifact.manifestBytes, {
1045
- flag: 'wx',
1046
- ...(signal === undefined ? {} : { signal }),
1047
- });
1186
+ const created = await publishExclusiveFile(artifact.manifestPath, artifact.manifestBytes, signal);
1187
+ if (created) {
1048
1188
  createdManifests.push(artifact.manifestPath);
1049
1189
  }
1050
- catch (error) {
1051
- if (error.code !== 'EEXIST') {
1052
- createdManifests.push(artifact.manifestPath);
1053
- }
1054
- throw error;
1190
+ else if (!(await existingManifestMatches(artifact.manifestPath, artifact.manifestBytes, signal))) {
1191
+ throw new ServiceError('ARTIFACT_MANIFEST_INVALID', 'Concurrent artifact provenance manifest publication did not complete');
1055
1192
  }
1056
1193
  }
1057
1194
  signal?.throwIfAborted();
@@ -1,6 +1,6 @@
1
1
  import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
2
2
  export const PACKAGE_NAME = 'hoi4-agent-tools';
3
- export const PACKAGE_VERSION = '2.5.0';
3
+ export const PACKAGE_VERSION = '2.5.1';
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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hoi4-agent-tools",
3
3
  "mcpName": "io.github.klimPaskov/hoi4-agent-tools",
4
- "version": "2.5.0",
4
+ "version": "2.5.1",
5
5
  "description": "Analyze HOI4 events, tech trees, AI weights, and MTTH; build focus trees, GUIs, and maps.",
6
6
  "type": "module",
7
7
  "license": "Apache-2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/configuration.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/configuration.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/continuous-focus-palette.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/continuous-focus-palette.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/custom-weighted-pool.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/custom-weighted-pool.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/event-feature-manifest.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/event-feature-manifest.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/focus-plan.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/focus-plan.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/focus-planning-sidecar.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/focus-planning-sidecar.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/gui-animation-source.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/gui-animation-source.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/gui-helper.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/gui-helper.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/gui-scenario.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/gui-scenario.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/map-operation.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/map-operation.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "oneOf": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/operation-result.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/operation-result.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-analysis-result.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-analysis-result.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-compare-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-compare-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-evaluate-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-evaluate-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-inspect-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-inspect-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-render-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-render-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-scenario-set.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-scenario-set.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-sequence-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-sequence-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-simulate-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-simulate-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.0/schemas/probability-sweep-input.schema.json",
2
+ "$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v2.5.1/schemas/probability-sweep-input.schema.json",
3
3
  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
4
  "type": "object",
5
5
  "properties": {
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.klimPaskov/hoi4-agent-tools",
4
4
  "title": "HOI4 Agent Tools",
5
5
  "description": "Analyze HOI4 events, tech trees, AI weights, and MTTH; build focus trees, GUIs, and maps.",
6
- "version": "2.5.0",
6
+ "version": "2.5.1",
7
7
  "repository": {
8
8
  "url": "https://github.com/klimPaskov/hoi4-agent-tools",
9
9
  "source": "github"
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "hoi4-agent-tools",
16
- "version": "2.5.0",
16
+ "version": "2.5.1",
17
17
  "transport": {
18
18
  "type": "stdio"
19
19
  }