hoi4-agent-tools 3.0.9 → 3.1.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 (124) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +4 -0
  3. package/dist/bin/stdio.js +23 -12
  4. package/dist/hoi4_agent_tools/core/analysis-checkpoints.js +82 -0
  5. package/dist/hoi4_agent_tools/core/artifact-resource.js +173 -0
  6. package/dist/hoi4_agent_tools/core/artifacts.js +84 -2
  7. package/dist/hoi4_agent_tools/core/dependency-pages.js +216 -0
  8. package/dist/hoi4_agent_tools/core/dependency-walk.js +266 -0
  9. package/dist/hoi4_agent_tools/core/domain-validation.js +188 -0
  10. package/dist/hoi4_agent_tools/core/engine.js +48 -5
  11. package/dist/hoi4_agent_tools/core/helper-expansion.js +186 -0
  12. package/dist/hoi4_agent_tools/core/index-segments.js +137 -0
  13. package/dist/hoi4_agent_tools/core/index.js +47 -4
  14. package/dist/hoi4_agent_tools/core/job-controls.js +109 -0
  15. package/dist/hoi4_agent_tools/core/job-executor.js +190 -0
  16. package/dist/hoi4_agent_tools/core/job-service.js +112 -0
  17. package/dist/hoi4_agent_tools/core/job-store.js +641 -0
  18. package/dist/hoi4_agent_tools/core/job-transactions.js +126 -0
  19. package/dist/hoi4_agent_tools/core/job-worker-host.js +200 -0
  20. package/dist/hoi4_agent_tools/core/job-worker.js +62 -0
  21. package/dist/hoi4_agent_tools/core/operation-context.js +31 -0
  22. package/dist/hoi4_agent_tools/core/operation-result.js +412 -0
  23. package/dist/hoi4_agent_tools/core/operation-tasks.js +382 -0
  24. package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +383 -0
  25. package/dist/hoi4_agent_tools/core/result.js +1 -0
  26. package/dist/hoi4_agent_tools/core/scanner.js +15 -75
  27. package/dist/hoi4_agent_tools/core/semantic-dependencies.js +233 -0
  28. package/dist/hoi4_agent_tools/core/shared-request-capacity.js +43 -11
  29. package/dist/hoi4_agent_tools/core/source/cache.js +107 -0
  30. package/dist/hoi4_agent_tools/core/source/parser.js +11 -1
  31. package/dist/hoi4_agent_tools/core/transaction-execution.js +134 -0
  32. package/dist/hoi4_agent_tools/core/transactions.js +80 -27
  33. package/dist/hoi4_agent_tools/event/graph.js +214 -290
  34. package/dist/hoi4_agent_tools/event/helper-expansion.js +33 -0
  35. package/dist/hoi4_agent_tools/event/job-operations.js +51 -0
  36. package/dist/hoi4_agent_tools/event/operations.js +179 -0
  37. package/dist/hoi4_agent_tools/event/service.js +67 -15
  38. package/dist/hoi4_agent_tools/event/source-analysis.js +5 -4
  39. package/dist/hoi4_agent_tools/focus/job-operations.js +77 -0
  40. package/dist/hoi4_agent_tools/focus/tool-operations.js +669 -0
  41. package/dist/hoi4_agent_tools/focus/tool-support.js +123 -0
  42. package/dist/hoi4_agent_tools/gui/job-operations.js +81 -0
  43. package/dist/hoi4_agent_tools/gui/tool-operations.js +270 -0
  44. package/dist/hoi4_agent_tools/map/job-operations.js +76 -0
  45. package/dist/hoi4_agent_tools/map/tool-operations.js +493 -0
  46. package/dist/hoi4_agent_tools/mcp/prompts/probability.js +25 -0
  47. package/dist/hoi4_agent_tools/mcp/prompts/register-modern.js +29 -0
  48. package/dist/hoi4_agent_tools/mcp/resources/register-modern.js +34 -0
  49. package/dist/hoi4_agent_tools/mcp/resources/register.js +4 -150
  50. package/dist/hoi4_agent_tools/mcp/server/base-tools.js +3 -219
  51. package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +188 -0
  52. package/dist/hoi4_agent_tools/mcp/server/create.js +16 -6
  53. package/dist/hoi4_agent_tools/mcp/server/instructions.js +2 -0
  54. package/dist/hoi4_agent_tools/mcp/server/job-task-store.js +67 -0
  55. package/dist/hoi4_agent_tools/mcp/server/legacy-task-tools.js +20 -0
  56. package/dist/hoi4_agent_tools/mcp/server/modern-tasks.js +168 -0
  57. package/dist/hoi4_agent_tools/mcp/server/progress.js +17 -15
  58. package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +43 -0
  59. package/dist/hoi4_agent_tools/mcp/server/result.js +2 -412
  60. package/dist/hoi4_agent_tools/mcp/server/task-request-context.js +9 -0
  61. package/dist/hoi4_agent_tools/mcp/server/task-tool-catalog.js +14 -0
  62. package/dist/hoi4_agent_tools/mcp/server/task-tool-definition.js +1 -0
  63. package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +1 -123
  64. package/dist/hoi4_agent_tools/mcp/tools/chaosx.js +38 -21
  65. package/dist/hoi4_agent_tools/mcp/tools/event.js +24 -315
  66. package/dist/hoi4_agent_tools/mcp/tools/focus.js +34 -1039
  67. package/dist/hoi4_agent_tools/mcp/tools/gui.js +19 -504
  68. package/dist/hoi4_agent_tools/mcp/tools/job.js +46 -0
  69. package/dist/hoi4_agent_tools/mcp/tools/map.js +21 -600
  70. package/dist/hoi4_agent_tools/mcp/tools/probability.js +39 -463
  71. package/dist/hoi4_agent_tools/mcp/tools/technology.js +40 -304
  72. package/dist/hoi4_agent_tools/mcp/transports/http.js +58 -1
  73. package/dist/hoi4_agent_tools/mcp/transports/modern-task-routing.js +109 -0
  74. package/dist/hoi4_agent_tools/mcp/transports/negotiated-stdio.js +121 -0
  75. package/dist/hoi4_agent_tools/probability/job-operations.js +58 -0
  76. package/dist/hoi4_agent_tools/probability/operations.js +183 -0
  77. package/dist/hoi4_agent_tools/probability/service.js +24 -3
  78. package/dist/hoi4_agent_tools/schemas/common.js +2 -0
  79. package/dist/hoi4_agent_tools/schemas/event.js +72 -1
  80. package/dist/hoi4_agent_tools/schemas/focus-requests.js +189 -0
  81. package/dist/hoi4_agent_tools/schemas/gui-requests.js +193 -0
  82. package/dist/hoi4_agent_tools/schemas/helper-expansion.js +58 -0
  83. package/dist/hoi4_agent_tools/schemas/map-requests.js +85 -0
  84. package/dist/hoi4_agent_tools/schemas/probability-requests.js +168 -0
  85. package/dist/hoi4_agent_tools/schemas/technology.js +90 -1
  86. package/dist/hoi4_agent_tools/schemas/transaction.js +1 -0
  87. package/dist/hoi4_agent_tools/technology/graph.js +83 -78
  88. package/dist/hoi4_agent_tools/technology/helper-expansion.js +33 -0
  89. package/dist/hoi4_agent_tools/technology/job-operations.js +60 -0
  90. package/dist/hoi4_agent_tools/technology/operations.js +125 -0
  91. package/dist/hoi4_agent_tools/technology/service.js +89 -15
  92. package/dist/hoi4_agent_tools/technology/source-analysis.js +2 -1
  93. package/dist/hoi4_agent_tools/version.js +1 -1
  94. package/docs/README.md +2 -0
  95. package/docs/development.md +2 -0
  96. package/docs/events.md +7 -2
  97. package/docs/helper-expansion.md +78 -0
  98. package/docs/http.md +2 -0
  99. package/docs/jobs.md +85 -0
  100. package/docs/technology.md +6 -2
  101. package/package.json +11 -5
  102. package/schemas/configuration.schema.json +1 -1
  103. package/schemas/continuous-focus-palette.schema.json +1 -1
  104. package/schemas/custom-weighted-pool.schema.json +1 -1
  105. package/schemas/event-feature-manifest.schema.json +1 -1
  106. package/schemas/focus-plan.schema.json +1 -1
  107. package/schemas/focus-planning-sidecar.schema.json +1 -1
  108. package/schemas/gui-animation-source.schema.json +1 -1
  109. package/schemas/gui-helper.schema.json +1 -1
  110. package/schemas/gui-scenario.schema.json +1 -1
  111. package/schemas/helper-expansion-request.schema.json +44 -0
  112. package/schemas/helper-expansion-summary.schema.json +70 -0
  113. package/schemas/map-operation.schema.json +1 -1
  114. package/schemas/operation-result.schema.json +1 -1
  115. package/schemas/probability-analysis-result.schema.json +1 -1
  116. package/schemas/probability-compare-input.schema.json +1 -1
  117. package/schemas/probability-evaluate-input.schema.json +1 -1
  118. package/schemas/probability-inspect-input.schema.json +1 -1
  119. package/schemas/probability-render-input.schema.json +1 -1
  120. package/schemas/probability-scenario-set.schema.json +1 -1
  121. package/schemas/probability-sequence-input.schema.json +1 -1
  122. package/schemas/probability-simulate-input.schema.json +1 -1
  123. package/schemas/probability-sweep-input.schema.json +1 -1
  124. package/server.json +2 -2
@@ -1,18 +1,8 @@
1
+ import { registerLegacyTaskTools } from '../server/legacy-task-tools.js';
1
2
  import { z } from 'zod/v4';
2
- import { boundedSourceHashEvidence, publicArtifactLink, } from '../../core/artifacts.js';
3
- import { canonicalJson, hashCanonical } from '../../core/canonical.js';
4
- import { IdleCacheLifetime } from '../../core/idle-cache-lifetime.js';
5
- import { emptyServiceResult } from '../../core/result.js';
6
- import { AgentNudger, allocateMapIdentifiersAsync, buildMapCatalog, exportProvinceGeometryRowRuns, lookupMapCoordinate, MAP_PROVINCE_GEOMETRY_SELECTOR_LIMIT, renderMap, searchMapCatalog, } from '../../map/index.js';
7
- import { workspaceIdSchema } from '../../schemas/common.js';
8
- import { mapAllocationRequestSchema, mapOperationSchema } from '../../schemas/map.js';
9
- import { PACKAGE_VERSION } from '../../version.js';
10
- import { requireServerScope, resolveServerWorkspaceId, } from '../server/base-tools.js';
11
- import { compactValidatedInputSchema } from '../server/context-schemas.js';
12
- import { autonomousFailureContext, autonomousResultArtifacts, executePlannedTransaction, } from '../server/transaction-execution.js';
3
+ import { mapInspectRequestSchema, mapRenderRequestSchema, mapRewriteRequestSchema, } from '../../schemas/map-requests.js';
13
4
  import { allocationEvidenceSchema, bitmapRenderHashesSchema, nonNegativeIntegerSchema, operationBlockerDataSchema, sha256Schema, } from '../server/output-schemas.js';
14
- import { progressReporter } from '../server/progress.js';
15
- import { errorResult, setInlineFilesScanned, strictOperationResultSchema, toolResult, } from '../server/result.js';
5
+ import { strictOperationResultSchema } from '../server/result.js';
16
6
  const changedBoundsSchema = z
17
7
  .object({
18
8
  minX: nonNegativeIntegerSchema,
@@ -85,12 +75,8 @@ const mapWorkspaceInspectOutputSchema = strictOperationResultSchema(z
85
75
  provinceGeometryCount: nonNegativeIntegerSchema,
86
76
  provinceGeometryPixelCount: nonNegativeIntegerSchema,
87
77
  provinceGeometryRowRunCount: nonNegativeIntegerSchema,
88
- unknownProvinceIds: z
89
- .array(nonNegativeIntegerSchema)
90
- .max(MAP_PROVINCE_GEOMETRY_SELECTOR_LIMIT),
91
- missingGeometryProvinceIds: z
92
- .array(nonNegativeIntegerSchema)
93
- .max(MAP_PROVINCE_GEOMETRY_SELECTOR_LIMIT),
78
+ unknownProvinceIds: z.array(nonNegativeIntegerSchema).max(512),
79
+ missingGeometryProvinceIds: z.array(nonNegativeIntegerSchema).max(512),
94
80
  })
95
81
  .strict());
96
82
  const mapRenderOutputSchema = strictOperationResultSchema(z
@@ -123,519 +109,28 @@ const artifactProducing = {
123
109
  idempotentHint: true,
124
110
  openWorldHint: false,
125
111
  };
126
- const mapInlineDiffLimit = 20;
127
- const mapInlineMembershipIdLimit = 32;
128
- const mapInlineTextLimit = 512;
129
- function compactAllocationEvidence(evidence) {
130
- return {
131
- ...evidence,
132
- rootCount: evidence.roots.length,
133
- roots: evidence.roots.slice(0, 16),
134
- rootsTruncated: evidence.roots.length > 16,
135
- };
136
- }
137
- function compactNullableText(value) {
138
- return value === null ? null : value.slice(0, mapInlineTextLimit);
139
- }
140
- function compactRecordDiffs(entries) {
141
- return entries.slice(0, mapInlineDiffLimit).map(({ key, before, after }) => ({
142
- key: key.slice(0, 256),
143
- before: compactNullableText(before),
144
- after: compactNullableText(after),
145
- }));
146
- }
147
- function compactMapSemanticDiff(semantic) {
148
- const counts = {
149
- definitions: semantic.definitions.length,
150
- stateMembership: semantic.stateMembership.length,
151
- regionMembership: semantic.regionMembership.length,
152
- states: semantic.states.length,
153
- ports: semantic.ports.length,
154
- buildingPositions: semantic.buildingPositions.length,
155
- unitPositions: semantic.unitPositions.length,
156
- weatherPositions: semantic.weatherPositions.length,
157
- entityLocators: semantic.entityLocators.length,
158
- supplyNodes: semantic.supplyNodes.length,
159
- railways: semantic.railways.length,
160
- adjacencies: semantic.adjacencies.length,
161
- normalAdjacencies: semantic.normalAdjacencies.length,
162
- };
163
- return {
164
- semantic: {
165
- definitions: semantic.definitions
166
- .slice(0, mapInlineDiffLimit)
167
- .map(({ id, before, after }) => ({
168
- id,
169
- before: compactNullableText(before),
170
- after: compactNullableText(after),
171
- })),
172
- stateMembership: semantic.stateMembership.slice(0, mapInlineDiffLimit).map((entry) => ({
173
- ...entry,
174
- before: entry.before.slice(0, mapInlineMembershipIdLimit),
175
- after: entry.after.slice(0, mapInlineMembershipIdLimit),
176
- })),
177
- regionMembership: semantic.regionMembership.slice(0, mapInlineDiffLimit).map((entry) => ({
178
- ...entry,
179
- before: entry.before.slice(0, mapInlineMembershipIdLimit),
180
- after: entry.after.slice(0, mapInlineMembershipIdLimit),
181
- })),
182
- states: compactRecordDiffs(semantic.states),
183
- ports: compactRecordDiffs(semantic.ports),
184
- buildingPositions: compactRecordDiffs(semantic.buildingPositions),
185
- unitPositions: compactRecordDiffs(semantic.unitPositions),
186
- weatherPositions: compactRecordDiffs(semantic.weatherPositions),
187
- entityLocators: compactRecordDiffs(semantic.entityLocators),
188
- supplyNodes: compactRecordDiffs(semantic.supplyNodes),
189
- railways: compactRecordDiffs(semantic.railways),
190
- adjacencies: compactRecordDiffs(semantic.adjacencies),
191
- normalAdjacencies: compactRecordDiffs(semantic.normalAdjacencies),
192
- supplyNodesChanged: semantic.supplyNodesChanged,
193
- railwaysChanged: semantic.railwaysChanged,
194
- adjacenciesChanged: semantic.adjacenciesChanged,
195
- normalAdjacenciesChanged: semantic.normalAdjacenciesChanged,
196
- },
197
- counts,
198
- truncated: Object.values(counts).some((count) => count > mapInlineDiffLimit) ||
199
- [...semantic.stateMembership, ...semantic.regionMembership].some(({ before, after }) => before.length > mapInlineMembershipIdLimit || after.length > mapInlineMembershipIdLimit) ||
200
- semantic.definitions.some(({ before, after }) => (before?.length ?? 0) > mapInlineTextLimit || (after?.length ?? 0) > mapInlineTextLimit) ||
201
- [
202
- ...semantic.states,
203
- ...semantic.ports,
204
- ...semantic.buildingPositions,
205
- ...semantic.unitPositions,
206
- ...semantic.weatherPositions,
207
- ...semantic.entityLocators,
208
- ...semantic.supplyNodes,
209
- ...semantic.railways,
210
- ...semantic.adjacencies,
211
- ...semantic.normalAdjacencies,
212
- ].some(({ key, before, after }) => key.length > 256 ||
213
- (before?.length ?? 0) > mapInlineTextLimit ||
214
- (after?.length ?? 0) > mapInlineTextLimit),
215
- };
216
- }
217
- const mapLayerSchema = z.enum([
218
- 'province',
219
- 'state',
220
- 'strategic-region',
221
- 'terrain',
222
- 'continent',
223
- 'owner',
224
- 'controller',
225
- 'cores',
226
- 'claims',
227
- 'coast',
228
- ]);
229
- const mapOverlaySchema = z.enum([
230
- 'coastlines',
231
- 'ports',
232
- 'victory-points',
233
- 'resources',
234
- 'state-buildings',
235
- 'province-buildings',
236
- 'supply-nodes',
237
- 'railways',
238
- 'adjacencies',
239
- 'building-positions',
240
- 'unit-positions',
241
- 'weather-positions',
242
- ]);
243
- const compactMapOperationSchema = compactValidatedInputSchema(mapOperationSchema, `Complete map operation: https://github.com/klimPaskov/hoi4-agent-tools/blob/v${PACKAGE_VERSION}/docs/map.md`);
244
- function validationSummary(validation) {
245
- return { passed: validation.passed, checks: validation.checks };
246
- }
247
- function mapArtifactSourceEvidence(files) {
248
- const complete = Object.fromEntries(files.map(({ displayPath, sha256 }) => [displayPath, sha256]));
249
- return { complete, bounded: boundedSourceHashEvidence(complete) };
250
- }
251
- function selectedMapEntities(snapshot, provinceIds, stateIds, regionIds) {
252
- const provinces = [...new Set(provinceIds)]
253
- .sort((a, b) => a - b)
254
- .map((id) => {
255
- const definition = snapshot.index.definitionsById.get(id);
256
- const geometry = snapshot.index.raster?.geometry.get(id);
257
- return {
258
- id,
259
- definition: definition === undefined
260
- ? null
261
- : {
262
- color: definition.color,
263
- type: definition.type,
264
- coastal: definition.coastal,
265
- terrain: definition.terrain,
266
- continent: definition.continent,
267
- },
268
- geometry: geometry ?? null,
269
- stateIds: snapshot.index.stateForProvince(id).map(({ id: state }) => state),
270
- regionIds: snapshot.index.regionForProvince(id).map(({ id: region }) => region),
271
- victoryPoints: snapshot.index.victoryPointsByProvince
272
- .get(id)
273
- ?.map(({ stateId: state, value }) => ({ stateId: state, value })) ?? [],
274
- provinceBuildings: snapshot.index.provinceBuildingsByProvince
275
- .get(id)
276
- ?.map(({ stateId: state, buildings }) => ({
277
- stateId: state,
278
- buildings: Object.fromEntries(buildings),
279
- })) ?? [],
280
- port: (() => {
281
- const port = snapshot.index.ports.find(({ provinceId }) => provinceId === id);
282
- return port === undefined
283
- ? null
284
- : {
285
- stateId: port.stateId,
286
- provinceId: port.provinceId,
287
- level: port.level,
288
- coastal: port.coastal,
289
- adjacentSeaProvinceIds: port.adjacentSeaProvinceIds,
290
- positions: port.positions.map(({ stateId, building, x, y, z, rotation, adjacentSeaProvince }) => ({
291
- stateId,
292
- building,
293
- x,
294
- y,
295
- z,
296
- rotation,
297
- adjacentSeaProvince,
298
- })),
299
- };
300
- })(),
301
- };
302
- });
303
- const states = [...new Set(stateIds)]
304
- .sort((a, b) => a - b)
305
- .map((id) => {
306
- const state = snapshot.index.statesById.get(id);
307
- return state === undefined
308
- ? { id, value: null }
309
- : {
310
- id,
311
- value: {
312
- name: state.name,
313
- capital: state.capital ?? null,
314
- manpower: state.manpower,
315
- category: state.category,
316
- provinces: state.provinces,
317
- resources: Object.fromEntries(state.resources),
318
- owner: state.owner ?? null,
319
- controller: state.controller ?? null,
320
- cores: state.cores,
321
- claims: state.claims,
322
- victoryPoints: state.victoryPoints.map(({ provinceId, value }) => ({
323
- provinceId,
324
- value,
325
- })),
326
- stateBuildings: Object.fromEntries(state.stateBuildings),
327
- provinceBuildings: Object.fromEntries([...state.provinceBuildings].map(([provinceId, buildings]) => [
328
- String(provinceId),
329
- Object.fromEntries(buildings),
330
- ])),
331
- },
332
- };
333
- });
334
- const regions = [...new Set(regionIds)]
335
- .sort((a, b) => a - b)
336
- .map((id) => {
337
- const region = snapshot.index.regionsById.get(id);
338
- return region === undefined
339
- ? { id, value: null }
340
- : {
341
- id,
342
- value: {
343
- name: region.name,
344
- provinces: region.provinces,
345
- navalTerrain: region.navalTerrain ?? null,
346
- },
347
- };
348
- });
349
- return { provinces, states, regions };
350
- }
351
- export function registerMapTools(server, engine, context) {
352
- const nudger = new AgentNudger(engine);
353
- const cacheLifetime = new IdleCacheLifetime(() => engine.releaseScanCaches());
354
- server.registerTool('hoi4.map_inspect', {
112
+ export const mapTaskTools = [
113
+ {
114
+ name: 'hoi4.map_inspect',
355
115
  title: 'Inspect HOI4 map',
356
116
  description: 'Render, validate, search, and click the complete map.',
357
- inputSchema: z
358
- .object({
359
- workspaceId: workspaceIdSchema,
360
- provinceIds: z
361
- .array(z.number().int().min(0))
362
- .max(MAP_PROVINCE_GEOMETRY_SELECTOR_LIMIT)
363
- .default([])
364
- .describe(`Export row runs for up to ${MAP_PROVINCE_GEOMETRY_SELECTOR_LIMIT} province IDs`),
365
- stateIds: z.array(z.number().int().positive()).max(1_000).default([]),
366
- regionIds: z.array(z.number().int().positive()).max(1_000).default([]),
367
- query: z.string().max(256).optional(),
368
- queryLimit: z.number().int().min(1).max(1_000).default(100),
369
- coordinates: z
370
- .array(z.discriminatedUnion('kind', [
371
- z
372
- .object({
373
- kind: z.literal('pixel'),
374
- x: z.number().int().min(0),
375
- y: z.number().int().min(0),
376
- })
377
- .strict(),
378
- z
379
- .object({
380
- kind: z.literal('map'),
381
- x: z.number(),
382
- z: z.number(),
383
- })
384
- .strict(),
385
- ]))
386
- .max(100)
387
- .default([]),
388
- includeOverview: z.boolean().default(true),
389
- allocationRequests: z.array(mapAllocationRequestSchema).max(100).default([]),
390
- })
391
- .strict(),
117
+ inputSchema: mapInspectRequestSchema,
392
118
  outputSchema: mapWorkspaceInspectOutputSchema,
393
119
  annotations: artifactProducing,
394
- }, async ({ workspaceId: requestedWorkspaceId, provinceIds, stateIds, regionIds, query, queryLimit, coordinates, includeOverview, allocationRequests, }, extra) => {
395
- const workspaceId = await resolveServerWorkspaceId(engine, context, requestedWorkspaceId, extra.signal);
396
- const releaseCaches = cacheLifetime.begin();
397
- try {
398
- const progress = progressReporter(extra);
399
- await progress.report(0, 3, 'Inspecting and validating the map');
400
- const { snapshot, validation } = await nudger.validate(workspaceId, context.principal, progress.signal);
401
- const sharedRevision = snapshot.revision;
402
- const catalog = buildMapCatalog(snapshot.index);
403
- const queryMatches = query === undefined ? [] : searchMapCatalog(catalog, query, queryLimit);
404
- const coordinateMatches = coordinates.map((coordinate) => lookupMapCoordinate(snapshot.index, coordinate));
405
- const overviewRendered = includeOverview && snapshot.index.raster !== undefined;
406
- const selected = selectedMapEntities(snapshot, provinceIds, stateIds, regionIds);
407
- const provinceGeometry = provinceIds.length === 0
408
- ? undefined
409
- : await exportProvinceGeometryRowRuns(snapshot.index, provinceIds, progress.signal);
410
- const allocationPreviews = [];
411
- for (const request of allocationRequests) {
412
- allocationPreviews.push({
413
- request,
414
- allocation: await allocateMapIdentifiersAsync(snapshot.index, request, progress.signal),
415
- });
416
- }
417
- const workspace = engine.resolver.get(workspaceId, context.principal);
418
- const sourceEvidence = mapArtifactSourceEvidence(snapshot.files);
419
- const inspectionProvenance = {
420
- kind: 'map-inspect',
421
- toolVersion: PACKAGE_VERSION,
422
- schemaVersion: 'map-inspect.v2',
423
- sourceHashes: sourceEvidence.bounded.sourceHashes,
424
- metadata: {
425
- sharedRevision,
426
- sourceHashInventory: sourceEvidence.bounded.inventory,
427
- },
428
- };
429
- const artifactWrites = [
430
- {
431
- name: `map-inspect.${snapshot.revision.slice(0, 16)}.json`,
432
- mimeType: 'application/json',
433
- content: `${canonicalJson({
434
- schemaVersion: 2,
435
- revision: snapshot.revision,
436
- sharedRevision,
437
- dimensions: snapshot.index.raster === undefined
438
- ? null
439
- : {
440
- width: snapshot.index.raster.width,
441
- height: snapshot.index.raster.height,
442
- bitsPerPixel: snapshot.index.provinceBitmap?.bitsPerPixel,
443
- dibSize: snapshot.index.provinceBitmap?.dibSize,
444
- },
445
- counts: {
446
- definitions: snapshot.index.definitions.length,
447
- states: snapshot.index.states.length,
448
- regions: snapshot.index.regions.length,
449
- adjacencies: snapshot.index.adjacencies.length,
450
- supplyNodes: snapshot.index.supplyNodes.length,
451
- railways: snapshot.index.railways.length,
452
- ports: snapshot.index.ports.length,
453
- locators: snapshot.index.entityLocators.length,
454
- },
455
- catalog,
456
- query: query ?? null,
457
- queryMatches,
458
- coordinateMatches,
459
- selected,
460
- allocationPreviews,
461
- validation,
462
- sourceHashes: sourceEvidence.complete,
463
- })}\n`,
464
- provenance: inspectionProvenance,
465
- description: 'Complete searchable map catalog, inspection, validation, selected records, and allocation previews',
466
- },
467
- ];
468
- if (overviewRendered) {
469
- const overviewLayer = 'state';
470
- const overviewOverlays = [
471
- 'coastlines',
472
- 'ports',
473
- 'victory-points',
474
- 'supply-nodes',
475
- 'railways',
476
- ];
477
- const overview = await renderMap(snapshot.index, {
478
- layer: overviewLayer,
479
- overlays: overviewOverlays,
480
- signal: progress.signal,
481
- });
482
- const overviewProvenance = {
483
- kind: 'map-inspect-overview',
484
- toolVersion: PACKAGE_VERSION,
485
- schemaVersion: 'map-inspect-overview.v1',
486
- sourceHashes: sourceEvidence.bounded.sourceHashes,
487
- renderProfile: {
488
- layer: overviewLayer,
489
- overlays: [...new Set(overviewOverlays)].sort(),
490
- scale: 1,
491
- },
492
- metadata: { sourceHashInventory: sourceEvidence.bounded.inventory },
493
- };
494
- artifactWrites.push({
495
- name: `map-overview.${snapshot.revision.slice(0, 16)}.png`,
496
- mimeType: 'image/png',
497
- content: overview.png,
498
- provenance: overviewProvenance,
499
- description: 'Complete rendered map overview',
500
- }, {
501
- name: `map-overview.${snapshot.revision.slice(0, 16)}.html`,
502
- mimeType: 'text/html',
503
- content: overview.html,
504
- provenance: overviewProvenance,
505
- description: 'Searchable and clickable full-map navigator with exact IDs, names, coordinates, and linked records',
506
- });
507
- }
508
- if (provinceGeometry !== undefined) {
509
- const { width, height, requestedProvinceIds, unknownProvinceIds, missingGeometryProvinceIds, pixelCount, rowRunCount, provinces, } = provinceGeometry;
510
- const selectorHash = hashCanonical(requestedProvinceIds).slice(0, 16);
511
- artifactWrites.push({
512
- name: `map-province-geometry.${snapshot.revision.slice(0, 16)}.${selectorHash}.json`,
513
- mimeType: 'application/json',
514
- content: `${canonicalJson({
515
- schemaVersion: 1,
516
- revision: snapshot.revision,
517
- dimensions: { width, height },
518
- coordinateSystem: {
519
- origin: 'top-left',
520
- xDirection: 'right',
521
- yDirection: 'down',
522
- },
523
- rowRunFormat: ['y', 'startX', 'endXExclusive'],
524
- requestedProvinceIds,
525
- unknownProvinceIds,
526
- missingGeometryProvinceIds,
527
- pixelCount,
528
- rowRunCount,
529
- provinces,
530
- sourceHashes: sourceEvidence.complete,
531
- })}\n`,
532
- provenance: {
533
- kind: 'map-province-geometry',
534
- toolVersion: PACKAGE_VERSION,
535
- schemaVersion: 'map-province-geometry.v1',
536
- sourceHashes: sourceEvidence.bounded.sourceHashes,
537
- metadata: {
538
- requestedProvinceIds,
539
- sourceHashInventory: sourceEvidence.bounded.inventory,
540
- },
541
- },
542
- description: 'Exact canonical province row runs for deriving bounded split or create geometry',
543
- });
544
- }
545
- const artifacts = await engine.artifacts.withAtomicChunkedWrites(workspace, artifactWrites, (stored) => Promise.resolve([...stored]), progress.signal);
546
- const result = emptyServiceResult(workspaceId, {
547
- revision: snapshot.revision,
548
- sharedRevision,
549
- width: snapshot.index.raster?.width ?? null,
550
- height: snapshot.index.raster?.height ?? null,
551
- definitions: snapshot.index.definitions.length,
552
- states: snapshot.index.states.length,
553
- regions: snapshot.index.regions.length,
554
- ports: snapshot.index.ports.length,
555
- inspectedProvinceCount: selected.provinces.length,
556
- inspectedStateCount: selected.states.length,
557
- inspectedRegionCount: selected.regions.length,
558
- allocationCount: allocationPreviews.length,
559
- queryMatchCount: queryMatches.length,
560
- coordinateMatchCount: coordinateMatches.length,
561
- overviewRendered,
562
- provinceGeometryCount: provinceGeometry?.provinces.length ?? 0,
563
- provinceGeometryPixelCount: provinceGeometry?.pixelCount ?? 0,
564
- provinceGeometryRowRunCount: provinceGeometry?.rowRunCount ?? 0,
565
- unknownProvinceIds: provinceGeometry?.unknownProvinceIds ?? [],
566
- missingGeometryProvinceIds: provinceGeometry?.missingGeometryProvinceIds ?? [],
567
- });
568
- result.code = 'MAP_INSPECTED';
569
- setInlineFilesScanned(result, snapshot.files.map(({ displayPath }) => displayPath));
570
- result.diagnostics = validation.diagnostics.slice(0, 100);
571
- result.artifacts = artifacts.map(publicArtifactLink);
572
- result.validation = validationSummary(validation);
573
- await progress.report(3, 3, 'Map inspection complete');
574
- return toolResult(result);
575
- }
576
- catch (error) {
577
- return errorResult(error, workspaceId);
578
- }
579
- finally {
580
- releaseCaches();
581
- }
582
- });
583
- server.registerTool('hoi4.map_render', {
120
+ },
121
+ {
122
+ name: 'hoi4.map_render',
584
123
  title: 'Render map inspection artifacts',
585
124
  description: 'Render searchable full-map PNG, JSON, and HTML.',
586
- inputSchema: z
587
- .object({
588
- workspaceId: workspaceIdSchema,
589
- layer: mapLayerSchema.optional(),
590
- overlays: z.array(mapOverlaySchema).max(12).optional(),
591
- scale: z.number().int().min(1).max(16).optional(),
592
- })
593
- .strict(),
125
+ inputSchema: mapRenderRequestSchema,
594
126
  outputSchema: mapRenderOutputSchema,
595
127
  annotations: artifactProducing,
596
- }, async ({ workspaceId: requestedWorkspaceId, layer, overlays, scale }, extra) => {
597
- const workspaceId = await resolveServerWorkspaceId(engine, context, requestedWorkspaceId, extra.signal);
598
- const releaseCaches = cacheLifetime.begin();
599
- try {
600
- const progress = progressReporter(extra);
601
- await progress.report(0, 3, 'Rendering offline map layer');
602
- const rendered = await nudger.renderAndStore(workspaceId, {
603
- ...(layer === undefined ? {} : { layer }),
604
- ...(overlays === undefined ? {} : { overlays }),
605
- ...(scale === undefined ? {} : { scale }),
606
- ...(context.principal === undefined ? {} : { principal: context.principal }),
607
- signal: progress.signal,
608
- });
609
- const result = emptyServiceResult(workspaceId, {
610
- revision: rendered.revision,
611
- width: rendered.bundle.width,
612
- height: rendered.bundle.height,
613
- hashes: rendered.bundle.hashes,
614
- offlineRepresentation: true,
615
- });
616
- result.code = 'MAP_RENDERED';
617
- setInlineFilesScanned(result, rendered.filesScanned);
618
- result.artifacts = rendered.artifacts.map(publicArtifactLink);
619
- await progress.report(3, 3, 'Map render complete');
620
- return toolResult(result);
621
- }
622
- catch (error) {
623
- return errorResult(error, workspaceId);
624
- }
625
- finally {
626
- releaseCaches();
627
- }
628
- });
629
- server.registerTool('hoi4.map_rewrite', {
128
+ },
129
+ {
130
+ name: 'hoi4.map_rewrite',
630
131
  title: 'Create or clean up map content',
631
132
  description: 'Apply map creation, edits, and ID swaps with visual and semantic evidence.',
632
- inputSchema: z
633
- .object({
634
- workspaceId: workspaceIdSchema,
635
- operations: z.array(compactMapOperationSchema).min(1).max(100),
636
- diffScale: z.number().int().min(1).max(16).optional(),
637
- })
638
- .strict(),
133
+ inputSchema: mapRewriteRequestSchema,
639
134
  outputSchema: mapPlanOutputSchema,
640
135
  annotations: {
641
136
  readOnlyHint: false,
@@ -643,82 +138,8 @@ export function registerMapTools(server, engine, context) {
643
138
  idempotentHint: false,
644
139
  openWorldHint: false,
645
140
  },
646
- }, async ({ workspaceId: requestedWorkspaceId, operations, diffScale }, extra) => {
647
- const workspaceId = await resolveServerWorkspaceId(engine, context, requestedWorkspaceId, extra.signal);
648
- const releaseCaches = cacheLifetime.begin();
649
- try {
650
- requireServerScope(context, 'hoi4:write');
651
- const progress = progressReporter(extra);
652
- await progress.report(0, 5, 'Planning map operations and visual diff');
653
- const typedOperations = operations;
654
- const planned = await nudger.planRewriteWithDiff({
655
- workspaceId,
656
- operations: typedOperations,
657
- ...(diffScale === undefined ? {} : { diffScale }),
658
- ...(context.principal === undefined ? {} : { principal: context.principal }),
659
- signal: progress.signal,
660
- });
661
- const diff = planned;
662
- await progress.report(3, 5, 'Preparing the map rewrite');
663
- await progress.report(4, 5, 'Applying and validating the map rewrite');
664
- const execution = await executePlannedTransaction(engine, planned.transaction, context.principal, progress.signal);
665
- const transaction = execution.transaction;
666
- const compactSemantic = compactMapSemanticDiff(diff.bundle.semantic);
667
- const result = emptyServiceResult(workspaceId, {
668
- execution: execution.outcome,
669
- allocations: planned.plan.allocations.slice(0, 100).map(compactAllocationEvidence),
670
- operationBlockers: planned.plan.blockers
671
- .slice(0, 100)
672
- .map(({ code, message, operationId }) => ({ code, message, operationId })),
673
- expectedChangedBounds: planned.plan.expectedChangedBounds ?? null,
674
- changedProvinceIds: diff.bundle.changedProvinceIds.slice(0, 256),
675
- changedProvinceCount: diff.bundle.changedProvinceIds.length,
676
- semanticDiff: compactSemantic.semantic,
677
- semanticDiffCounts: compactSemantic.counts,
678
- semanticDiffTruncated: compactSemantic.truncated,
679
- fileCount: transaction.files.length,
680
- artifactCount: transaction.artifacts.length,
681
- });
682
- result.status = transaction.validation.passed ? 'ok' : 'blocked';
683
- result.code =
684
- execution.outcome === 'applied'
685
- ? 'MAP_CHANGES_APPLIED'
686
- : execution.outcome === 'unchanged'
687
- ? 'MAP_CHANGES_UNCHANGED'
688
- : 'MAP_CHANGES_BLOCKED';
689
- setInlineFilesScanned(result, diff.filesScanned);
690
- result.proposedFiles = transaction.files
691
- .slice(0, 100)
692
- .map(({ relativePath }) => relativePath);
693
- result.changedFiles = transaction.appliedFiles.slice(0, 100);
694
- result.diagnostics = transaction.diagnostics.slice(0, 100);
695
- result.artifacts = autonomousResultArtifacts(execution);
696
- result.validation = transaction.validation;
697
- result.blockers = [
698
- ...planned.plan.blockers.map(({ code, message }) => ({
699
- code,
700
- message,
701
- })),
702
- ...transaction.diagnostics
703
- .filter(({ severity }) => severity === 'error' || severity === 'blocker')
704
- .map(({ code, message, details }) => ({
705
- code,
706
- message,
707
- ...(details === undefined ? {} : { details }),
708
- })),
709
- ].slice(0, 100);
710
- await progress.report(5, 5, execution.outcome === 'applied'
711
- ? 'Map rewrite complete'
712
- : execution.outcome === 'unchanged'
713
- ? 'Map content already satisfied the operations'
714
- : 'Map rewrite blocked');
715
- return toolResult(result);
716
- }
717
- catch (error) {
718
- return errorResult(error, workspaceId, autonomousFailureContext(error));
719
- }
720
- finally {
721
- releaseCaches();
722
- }
723
- });
141
+ },
142
+ ];
143
+ export function registerMapTools(server, _engine, _context) {
144
+ registerLegacyTaskTools(server, mapTaskTools);
724
145
  }