hoi4-agent-tools 3.10.0 → 4.0.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.
Files changed (163) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/README.md +91 -147
  3. package/dist/bin/http.js +10 -0
  4. package/dist/bin/setup.js +3 -2
  5. package/dist/bin/stdio.js +10 -0
  6. package/dist/hoi4_agent_tools/core/artifacts.js +40 -5
  7. package/dist/hoi4_agent_tools/core/canonical.js +18 -4
  8. package/dist/hoi4_agent_tools/core/condition-evaluator.js +18 -0
  9. package/dist/hoi4_agent_tools/core/configuration.js +7 -0
  10. package/dist/hoi4_agent_tools/core/domain-scan-patterns.js +12 -0
  11. package/dist/hoi4_agent_tools/core/domain-tools.js +65 -0
  12. package/dist/hoi4_agent_tools/core/domain-validation.js +5 -2
  13. package/dist/hoi4_agent_tools/core/engine.js +29 -2
  14. package/dist/hoi4_agent_tools/core/index.js +2 -2
  15. package/dist/hoi4_agent_tools/core/job-executor.js +14 -1
  16. package/dist/hoi4_agent_tools/core/job-operation-registration.js +15 -7
  17. package/dist/hoi4_agent_tools/core/job-store.js +78 -5
  18. package/dist/hoi4_agent_tools/core/job-transactions.js +31 -2
  19. package/dist/hoi4_agent_tools/core/job-worker-host.js +314 -127
  20. package/dist/hoi4_agent_tools/core/job-worker.js +96 -37
  21. package/dist/hoi4_agent_tools/core/operation-result.js +123 -10
  22. package/dist/hoi4_agent_tools/core/operation-tasks.js +49 -89
  23. package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +25 -21
  24. package/dist/hoi4_agent_tools/core/server-registry.js +22 -5
  25. package/dist/hoi4_agent_tools/core/shared-request-capacity.js +26 -1
  26. package/dist/hoi4_agent_tools/core/source/cache.js +20 -10
  27. package/dist/hoi4_agent_tools/core/source/lexer.js +11 -3
  28. package/dist/hoi4_agent_tools/core/temporary-directories.js +293 -0
  29. package/dist/hoi4_agent_tools/core/transactions.js +114 -23
  30. package/dist/hoi4_agent_tools/core/worker-exit.js +80 -0
  31. package/dist/hoi4_agent_tools/core/workspace.js +84 -8
  32. package/dist/hoi4_agent_tools/decision/ai.js +1 -1
  33. package/dist/hoi4_agent_tools/decision/service.js +28 -0
  34. package/dist/hoi4_agent_tools/decision/source-inventory.js +7 -5
  35. package/dist/hoi4_agent_tools/event/job-operations.js +9 -5
  36. package/dist/hoi4_agent_tools/event/operations.js +14 -1
  37. package/dist/hoi4_agent_tools/focus/job-operations.js +1 -2
  38. package/dist/hoi4_agent_tools/focus/tool-operations.js +1 -1
  39. package/dist/hoi4_agent_tools/gui/assets.js +3 -0
  40. package/dist/hoi4_agent_tools/gui/dds.js +8 -4
  41. package/dist/hoi4_agent_tools/gui/layout.js +45 -5
  42. package/dist/hoi4_agent_tools/gui/native-state.js +244 -0
  43. package/dist/hoi4_agent_tools/gui/renderer.js +12 -7
  44. package/dist/hoi4_agent_tools/gui/source-graph.js +17 -4
  45. package/dist/hoi4_agent_tools/gui/studio.js +33 -21
  46. package/dist/hoi4_agent_tools/gui/validators.js +8 -6
  47. package/dist/hoi4_agent_tools/map/bmp.js +1 -1
  48. package/dist/hoi4_agent_tools/map/derived.js +748 -0
  49. package/dist/hoi4_agent_tools/map/job-operations.js +7 -3
  50. package/dist/hoi4_agent_tools/map/model.js +103 -0
  51. package/dist/hoi4_agent_tools/map/operations.js +427 -58
  52. package/dist/hoi4_agent_tools/map/references.js +483 -0
  53. package/dist/hoi4_agent_tools/map/service.js +136 -61
  54. package/dist/hoi4_agent_tools/map/tool-operations.js +2 -1
  55. package/dist/hoi4_agent_tools/map/validation.js +3 -0
  56. package/dist/hoi4_agent_tools/map/world.js +1280 -0
  57. package/dist/hoi4_agent_tools/mcp/prompts/probability.js +2 -2
  58. package/dist/hoi4_agent_tools/mcp/server/argument-errors.js +72 -0
  59. package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +23 -5
  60. package/dist/hoi4_agent_tools/mcp/server/create.js +7 -0
  61. package/dist/hoi4_agent_tools/mcp/server/instructions.js +1 -1
  62. package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +24 -30
  63. package/dist/hoi4_agent_tools/mcp/server/tool-listing.js +45 -0
  64. package/dist/hoi4_agent_tools/mcp/tools/analysis.js +28 -8
  65. package/dist/hoi4_agent_tools/mcp/tools/event.js +19 -30
  66. package/dist/hoi4_agent_tools/mcp/tools/focus.js +5 -13
  67. package/dist/hoi4_agent_tools/mcp/tools/gui.js +6 -6
  68. package/dist/hoi4_agent_tools/mcp/tools/job.js +4 -4
  69. package/dist/hoi4_agent_tools/mcp/tools/map.js +15 -18
  70. package/dist/hoi4_agent_tools/mcp/tools/probability.js +42 -48
  71. package/dist/hoi4_agent_tools/mcp/tools/reference.js +26 -9
  72. package/dist/hoi4_agent_tools/mcp/tools/technology.js +17 -30
  73. package/dist/hoi4_agent_tools/mcp/transports/http.js +28 -4
  74. package/dist/hoi4_agent_tools/mcp/transports/stdio-lifetime.js +31 -8
  75. package/dist/hoi4_agent_tools/probability/job-operations.js +4 -9
  76. package/dist/hoi4_agent_tools/probability/operations.js +175 -13
  77. package/dist/hoi4_agent_tools/probability/source-analysis.js +24 -1
  78. package/dist/hoi4_agent_tools/reference/error-log.js +359 -0
  79. package/dist/hoi4_agent_tools/reference/known-mistakes.js +307 -0
  80. package/dist/hoi4_agent_tools/reference/known-mistakes.json +154 -0
  81. package/dist/hoi4_agent_tools/reference/mod-index.js +101 -0
  82. package/dist/hoi4_agent_tools/reference/operations.js +88 -19
  83. package/dist/hoi4_agent_tools/reference/player-text.js +4 -3
  84. package/dist/hoi4_agent_tools/reference/script-validation.js +19 -1
  85. package/dist/hoi4_agent_tools/reference/service.js +6 -0
  86. package/dist/hoi4_agent_tools/runtime.js +14 -1
  87. package/dist/hoi4_agent_tools/schemas/event.js +78 -19
  88. package/dist/hoi4_agent_tools/schemas/focus-requests.js +10 -0
  89. package/dist/hoi4_agent_tools/schemas/lenient-arguments.js +266 -0
  90. package/dist/hoi4_agent_tools/schemas/map-requests.js +63 -20
  91. package/dist/hoi4_agent_tools/schemas/map.js +122 -3
  92. package/dist/hoi4_agent_tools/schemas/mod-tools.js +181 -0
  93. package/dist/hoi4_agent_tools/schemas/probability-requests.js +119 -9
  94. package/dist/hoi4_agent_tools/schemas/reference.js +13 -0
  95. package/dist/hoi4_agent_tools/schemas/scenarios.js +1 -3
  96. package/dist/hoi4_agent_tools/schemas/script-validation.js +13 -6
  97. package/dist/hoi4_agent_tools/schemas/technology.js +79 -21
  98. package/dist/hoi4_agent_tools/technology/job-operations.js +3 -4
  99. package/dist/hoi4_agent_tools/technology/operations.js +14 -1
  100. package/dist/hoi4_agent_tools/version.js +1 -1
  101. package/docs/README.md +7 -4
  102. package/docs/analysis.md +15 -2
  103. package/docs/configuration.md +281 -0
  104. package/docs/development.md +26 -2
  105. package/docs/events.md +29 -6
  106. package/docs/examples.md +13 -28
  107. package/docs/focus.md +127 -5
  108. package/docs/gui.md +105 -21
  109. package/docs/helper-expansion.md +9 -9
  110. package/docs/http.md +3 -1
  111. package/docs/jobs.md +23 -14
  112. package/docs/map.md +199 -7
  113. package/docs/mechanics.md +6 -2
  114. package/docs/probability.md +46 -16
  115. package/docs/reference.md +73 -5
  116. package/docs/setup.md +45 -14
  117. package/docs/technology.md +28 -4
  118. package/docs/third-party-notices.md +11 -0
  119. package/docs/tools.md +150 -42
  120. package/docs/troubleshooting.md +61 -0
  121. package/examples/clients/codex-config.toml +5 -2
  122. package/examples/clients/generic.json +2 -2
  123. package/examples/config/config.json +7 -0
  124. package/npm-shrinkwrap.json +151 -156
  125. package/package.json +13 -8
  126. package/schemas/configuration.schema.json +144 -128
  127. package/schemas/continuous-focus-palette.schema.json +1 -1
  128. package/schemas/custom-weighted-pool.schema.json +1 -1
  129. package/schemas/decision-inspect-input.schema.json +1 -1
  130. package/schemas/error-log-input.schema.json +55 -0
  131. package/schemas/event-feature-manifest.schema.json +1 -1
  132. package/schemas/focus-plan.schema.json +1 -1
  133. package/schemas/focus-planning-sidecar.schema.json +1 -1
  134. package/schemas/gui-animation-source.schema.json +1 -1
  135. package/schemas/gui-helper.schema.json +1 -1
  136. package/schemas/gui-scenario.schema.json +1 -1
  137. package/schemas/helper-expansion-request.schema.json +1 -1
  138. package/schemas/helper-expansion-summary.schema.json +1 -1
  139. package/schemas/impact-inspect-input.schema.json +1 -1
  140. package/schemas/map-operation.schema.json +597 -126
  141. package/schemas/mechanic-test-input.schema.json +1 -1
  142. package/schemas/mod-index-input.schema.json +51 -0
  143. package/schemas/operation-result.schema.json +2 -2
  144. package/schemas/package-check-input.schema.json +1 -1
  145. package/schemas/package-manifest.schema.json +1 -1
  146. package/schemas/probability-analysis-result.schema.json +1 -1
  147. package/schemas/probability-compare-input.schema.json +1 -1
  148. package/schemas/probability-evaluate-input.schema.json +1 -1
  149. package/schemas/probability-inspect-input.schema.json +1 -1
  150. package/schemas/probability-render-input.schema.json +1 -1
  151. package/schemas/probability-scenario-set.schema.json +1 -1
  152. package/schemas/probability-sequence-input.schema.json +1 -1
  153. package/schemas/probability-simulate-input.schema.json +1 -1
  154. package/schemas/probability-sweep-input.schema.json +1 -1
  155. package/schemas/reference-context-input.schema.json +1 -1
  156. package/schemas/reference-read-input.schema.json +1 -1
  157. package/schemas/reference-search-input.schema.json +1 -1
  158. package/schemas/scenario-suite.schema.json +3 -5
  159. package/schemas/scenario-test-input.schema.json +3 -5
  160. package/schemas/script-validate-input.schema.json +1 -1
  161. package/schemas/shared-scenario.schema.json +1 -1
  162. package/schemas/source-lookup-input.schema.json +1 -1
  163. package/server.json +3 -3
@@ -1,3 +1,4 @@
1
+ import { MAP_REFERENCE_SCRIPT_PATTERNS, mapScriptConsistency } from './references.js';
1
2
  import path from 'node:path';
2
3
  import { escapeFileGlob } from '../core/file-glob.js';
3
4
  import { ArtifactStore, boundedSourceHashEvidence, publicArtifactLink, } from '../core/artifacts.js';
@@ -381,6 +382,8 @@ function sourceTextPatterns(roots) {
381
382
  return [
382
383
  'common/bookmarks/**/*.txt',
383
384
  'common/terrain/*.txt',
385
+ 'common/buildings/*.txt',
386
+ 'descriptor.mod',
384
387
  ...roots.map.flatMap((root) => {
385
388
  const normalized = normalizeSourceRoot(root);
386
389
  return ['map', 'csv', 'txt'].map((extension) => `${normalized}/**/*.${extension}`);
@@ -396,18 +399,24 @@ function provinceBitmapPatterns(defaultMaps, mapRoots) {
396
399
  const activeByPath = new Map(defaultMaps
397
400
  .filter(({ shadowedBy }) => shadowedBy === undefined)
398
401
  .map((file) => [file.relativePath.replaceAll('\\', '/').toLowerCase(), file]));
399
- let activeName = 'provinces.bmp';
400
- for (const root of mapRoots) {
401
- const relativePath = `${normalizeSourceRoot(root)}/default.map`.toLowerCase();
402
- const active = activeByPath.get(relativePath);
403
- if (active === undefined)
404
- continue;
405
- activeName = defaultMapSelectorValue(active, 'provinces', activeName);
406
- break;
407
- }
408
- const names = new Set([activeName]);
409
- for (const file of defaultMaps) {
410
- names.add(defaultMapSelectorValue(file, 'provinces', activeName));
402
+ // The province bitmap, and the heightmap that gives generated positions their height.
403
+ const names = new Set();
404
+ for (const [selector, fallback] of [
405
+ ['provinces', 'provinces.bmp'],
406
+ ['heightmap', 'heightmap.bmp'],
407
+ ]) {
408
+ let activeName = fallback;
409
+ for (const root of mapRoots) {
410
+ const relativePath = `${normalizeSourceRoot(root)}/default.map`.toLowerCase();
411
+ const active = activeByPath.get(relativePath);
412
+ if (active === undefined)
413
+ continue;
414
+ activeName = defaultMapSelectorValue(active, selector, activeName);
415
+ break;
416
+ }
417
+ names.add(activeName);
418
+ for (const file of defaultMaps)
419
+ names.add(defaultMapSelectorValue(file, selector, activeName));
411
420
  }
412
421
  const patterns = new Set();
413
422
  for (const root of mapRoots) {
@@ -463,6 +472,43 @@ function validationWithPlan(validation, plan) {
463
472
  checks,
464
473
  };
465
474
  }
475
+ /**
476
+ * A map whose bitmap changed size (a new world) cannot be diffed pixel by pixel; its review
477
+ * image is the whole proposed map, and every province counts as changed.
478
+ */
479
+ async function wholeMapDiff(after, options) {
480
+ const rendered = await renderMap(after, {
481
+ layer: 'state',
482
+ overlays: ['coastlines', 'ports', 'victory-points', 'supply-nodes', 'railways'],
483
+ ...(options?.scale === undefined ? {} : { scale: options.scale }),
484
+ ...(options?.signal === undefined ? {} : { signal: options.signal }),
485
+ ...(options?.budget === undefined ? {} : { budget: options.budget }),
486
+ });
487
+ return {
488
+ ...rendered,
489
+ changedProvinceIds: after.definitions.map(({ id }) => id).filter((id) => id > 0),
490
+ semantic: {
491
+ definitions: [],
492
+ stateMembership: [],
493
+ regionMembership: [],
494
+ states: [],
495
+ ports: [],
496
+ buildingPositions: [],
497
+ unitPositions: [],
498
+ weatherPositions: [],
499
+ entityLocators: [],
500
+ supplyNodes: [],
501
+ railways: [],
502
+ adjacencies: [],
503
+ normalAdjacencies: [],
504
+ supplyNodesChanged: true,
505
+ railwaysChanged: true,
506
+ adjacenciesChanged: true,
507
+ normalAdjacenciesChanged: true,
508
+ },
509
+ ...(options?.review === undefined ? {} : { review: options.review }),
510
+ };
511
+ }
466
512
  export class AgentNudger {
467
513
  engine;
468
514
  resolver;
@@ -497,17 +543,7 @@ export class AgentNudger {
497
543
  const locatorSnapshot = await this.engine.scan(workspaceId, { patterns: ['**/*.asset'], rootKinds: ['mod', 'fixture'] }, principal, signal);
498
544
  const externalScriptSnapshot = includeExternalScriptReferences
499
545
  ? await this.engine.scan(workspaceId, {
500
- patterns: [
501
- 'events/**/*.txt',
502
- 'common/national_focus/**/*.txt',
503
- 'common/decisions/**/*.txt',
504
- 'common/scripted_effects/**/*.txt',
505
- 'common/scripted_triggers/**/*.txt',
506
- 'common/on_actions/**/*.txt',
507
- 'common/ideas/**/*.txt',
508
- 'common/characters/**/*.txt',
509
- 'history/countries/**/*.txt',
510
- ],
546
+ patterns: [...MAP_REFERENCE_SCRIPT_PATTERNS],
511
547
  }, principal, signal)
512
548
  : undefined;
513
549
  signal?.throwIfAborted();
@@ -524,20 +560,43 @@ export class AgentNudger {
524
560
  index: MapWorkspaceIndex.build(files, sourceRoots, SymbolIndex.build(files)),
525
561
  };
526
562
  }
527
- async validate(workspaceId, principal, signal) {
528
- const snapshot = await this.scan(workspaceId, principal, signal);
563
+ async validate(workspaceId, principal, signal, scripts = false) {
564
+ const snapshot = await this.scan(workspaceId, principal, signal, scripts);
565
+ const validation = await validateMapAsync(snapshot.index, signal === undefined ? {} : { signal });
566
+ if (!scripts)
567
+ return { snapshot, validation };
568
+ const consistency = mapScriptConsistency(snapshot.index);
569
+ const diagnostics = sortDiagnostics([...validation.diagnostics, ...consistency.diagnostics]);
529
570
  return {
530
571
  snapshot,
531
- validation: await validateMapAsync(snapshot.index, signal === undefined ? {} : { signal }),
572
+ validation: {
573
+ ...validation,
574
+ passed: validation.passed &&
575
+ !consistency.diagnostics.some(({ severity }) => severity === 'error'),
576
+ diagnostics,
577
+ checks: [
578
+ ...validation.checks,
579
+ {
580
+ id: 'map-script-references',
581
+ passed: consistency.missing === 0,
582
+ message: `${consistency.references} script reference(s) to map entities checked; ${consistency.missing} name an undefined state, province or region`,
583
+ },
584
+ ],
585
+ },
586
+ scriptReferences: { references: consistency.references, missing: consistency.missing },
532
587
  };
533
588
  }
534
589
  async prepareOperations(workspaceId, operations, principal, signal) {
535
590
  signal?.throwIfAborted();
536
- const snapshot = await this.scan(workspaceId, principal, signal, operations.some(({ kind }) => kind === 'renumber_map_entity'));
591
+ const snapshot = await this.scan(workspaceId, principal, signal, operations.some((operation) => operation.kind === 'renumber_map_entity' ||
592
+ operation.kind === 'remap_map_references' ||
593
+ 'remapReferences' in operation));
537
594
  const plan = await planMapOperationsAsync(snapshot.index, operations, signal);
538
595
  const baselineValidation = await validateMapAsync(snapshot.index, signal === undefined ? {} : { signal });
596
+ // A new world has nothing to compare its bitmap format and pixels with.
597
+ const newWorld = operations.some(({ kind }) => kind === 'create_world');
539
598
  const rawValidation = await validateMapAsync(plan.finalIndex, {
540
- baseline: snapshot.index,
599
+ ...(newWorld ? {} : { baseline: snapshot.index }),
541
600
  ...(signal === undefined ? {} : { signal }),
542
601
  ...(plan.expectedChangedBounds === undefined
543
602
  ? {}
@@ -739,20 +798,35 @@ export class AgentNudger {
739
798
  'victory-points',
740
799
  'weather-positions',
741
800
  ];
801
+ const hasBeforeMap = snapshot.index.raster !== undefined ||
802
+ snapshot.index.diagnostics.some(({ code }) => code.startsWith('RENDER_'));
803
+ const sameSize = snapshot.index.provinceBitmap !== undefined &&
804
+ snapshot.index.provinceBitmap.width === plan.finalIndex.provinceBitmap?.width &&
805
+ snapshot.index.provinceBitmap.height === plan.finalIndex.provinceBitmap.height;
742
806
  const [rawBundle, rawBeforeBundle, rawProposedBundle] = await Promise.all([
743
- renderMapDiff(snapshot.index, plan.finalIndex, {
744
- ...(options.scale === undefined ? {} : { scale: options.scale }),
745
- ...(options.signal === undefined ? {} : { signal: options.signal }),
746
- budget,
747
- review,
748
- }),
749
- renderMap(snapshot.index, {
750
- layer: 'state',
751
- overlays: [...comparisonOverlays],
752
- ...(options.scale === undefined ? {} : { scale: options.scale }),
753
- ...(options.signal === undefined ? {} : { signal: options.signal }),
754
- budget,
755
- }),
807
+ sameSize
808
+ ? renderMapDiff(snapshot.index, plan.finalIndex, {
809
+ ...(options.scale === undefined ? {} : { scale: options.scale }),
810
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
811
+ budget,
812
+ review,
813
+ })
814
+ : wholeMapDiff(plan.finalIndex, {
815
+ ...(options.scale === undefined ? {} : { scale: options.scale }),
816
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
817
+ budget,
818
+ review,
819
+ }),
820
+ // A new world over a game without any map has nothing to draw as "before".
821
+ hasBeforeMap
822
+ ? renderMap(snapshot.index, {
823
+ layer: 'state',
824
+ overlays: [...comparisonOverlays],
825
+ ...(options.scale === undefined ? {} : { scale: options.scale }),
826
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
827
+ budget,
828
+ })
829
+ : Promise.resolve(undefined),
756
830
  renderMap(plan.finalIndex, {
757
831
  layer: 'state',
758
832
  overlays: [...comparisonOverlays],
@@ -764,7 +838,9 @@ export class AgentNudger {
764
838
  const completeSourceHashes = sourceHashes(snapshot);
765
839
  const sourceEvidence = boundedSourceHashEvidence(completeSourceHashes);
766
840
  const bundle = mapBundleWithSourceHashes(rawBundle, completeSourceHashes);
767
- const beforeBundle = mapBundleWithSourceHashes(rawBeforeBundle, completeSourceHashes);
841
+ const beforeBundle = rawBeforeBundle === undefined
842
+ ? undefined
843
+ : mapBundleWithSourceHashes(rawBeforeBundle, completeSourceHashes);
768
844
  const proposedBundle = mapBundleWithSourceHashes(rawProposedBundle, completeSourceHashes);
769
845
  const workspace = this.resolver.get(snapshot.workspaceId, options.principal);
770
846
  const provenance = {
@@ -816,24 +892,6 @@ export class AgentNudger {
816
892
  content: bundle.html,
817
893
  provenance,
818
894
  },
819
- {
820
- name: 'map-before.png',
821
- mimeType: 'image/png',
822
- content: beforeBundle.png,
823
- provenance: comparisonProvenance('before'),
824
- },
825
- {
826
- name: 'map-before.json',
827
- mimeType: 'application/json',
828
- content: beforeBundle.json,
829
- provenance: comparisonProvenance('before'),
830
- },
831
- {
832
- name: 'map-before.html',
833
- mimeType: 'text/html',
834
- content: beforeBundle.html,
835
- provenance: comparisonProvenance('before'),
836
- },
837
895
  {
838
896
  name: 'map-proposed.png',
839
897
  mimeType: 'image/png',
@@ -853,10 +911,27 @@ export class AgentNudger {
853
911
  provenance: comparisonProvenance('proposed'),
854
912
  },
855
913
  ];
914
+ if (beforeBundle !== undefined)
915
+ writes.splice(3, 0, {
916
+ name: 'map-before.png',
917
+ mimeType: 'image/png',
918
+ content: beforeBundle.png,
919
+ provenance: comparisonProvenance('before'),
920
+ }, {
921
+ name: 'map-before.json',
922
+ mimeType: 'application/json',
923
+ content: beforeBundle.json,
924
+ provenance: comparisonProvenance('before'),
925
+ }, {
926
+ name: 'map-before.html',
927
+ mimeType: 'text/html',
928
+ content: beforeBundle.html,
929
+ provenance: comparisonProvenance('before'),
930
+ });
856
931
  const artifacts = await this.artifacts.withAtomicChunkedWrites(workspace, writes, (stored) => Promise.resolve([...stored]), options.signal);
857
932
  return {
858
933
  bundle,
859
- beforeBundle,
934
+ ...(beforeBundle === undefined ? {} : { beforeBundle }),
860
935
  proposedBundle,
861
936
  plan,
862
937
  artifacts,
@@ -362,7 +362,7 @@ export async function inspectMap(engine, nudger, input, context) {
362
362
  await context.progress(3, 3, 'Map lookup complete');
363
363
  return result;
364
364
  }
365
- const { snapshot, validation } = await nudger.validate(context.workspaceId, context.principal, context.signal);
365
+ const { snapshot, validation, scriptReferences } = await nudger.validate(context.workspaceId, context.principal, context.signal, input.references);
366
366
  const sharedRevision = snapshot.revision;
367
367
  const catalog = buildMapCatalog(snapshot.index);
368
368
  const queryMatches = input.query === undefined ? [] : searchMapCatalog(catalog, input.query, input.queryLimit);
@@ -522,6 +522,7 @@ export async function inspectMap(engine, nudger, input, context) {
522
522
  provinceGeometryRowRunCount: provinceGeometry?.rowRunCount ?? 0,
523
523
  unknownProvinceIds: provinceGeometry?.unknownProvinceIds ?? [],
524
524
  missingGeometryProvinceIds: provinceGeometry?.missingGeometryProvinceIds ?? [],
525
+ ...(scriptReferences === undefined ? {} : { scriptReferences }),
525
526
  });
526
527
  result.code = 'MAP_INSPECTED';
527
528
  setInlineFilesScanned(result, snapshot.files.map(({ displayPath }) => displayPath));
@@ -546,6 +546,9 @@ function* validateDefinitions(index, diagnostics, options) {
546
546
  }
547
547
  for (const [definitionIndex, definition] of index.definitions.entries()) {
548
548
  yield* cancellationCheckpoint(options.signal, definitionIndex, 256);
549
+ // Province 0 is the reserved null row (0;0;0;0;land;false;unknown;0) the game expects.
550
+ if (definition.id === 0 && definition.terrain === 'unknown')
551
+ continue;
549
552
  const terrain = index.terrainCategories?.get(definition.terrain);
550
553
  if (index.terrainCategories !== undefined && terrain === undefined) {
551
554
  addDiagnostic(diagnostics, options, {