hoi4-agent-tools 3.0.8 → 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 (135) hide show
  1. package/CHANGELOG.md +35 -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/condition-evaluator.js +399 -0
  8. package/dist/hoi4_agent_tools/core/condition-model.js +1 -0
  9. package/dist/hoi4_agent_tools/core/condition-schema.js +42 -0
  10. package/dist/hoi4_agent_tools/core/dependency-pages.js +216 -0
  11. package/dist/hoi4_agent_tools/core/dependency-walk.js +266 -0
  12. package/dist/hoi4_agent_tools/core/domain-validation.js +188 -0
  13. package/dist/hoi4_agent_tools/core/engine.js +48 -5
  14. package/dist/hoi4_agent_tools/core/helper-expansion.js +186 -0
  15. package/dist/hoi4_agent_tools/core/index-segments.js +137 -0
  16. package/dist/hoi4_agent_tools/core/index.js +47 -4
  17. package/dist/hoi4_agent_tools/core/job-controls.js +109 -0
  18. package/dist/hoi4_agent_tools/core/job-executor.js +190 -0
  19. package/dist/hoi4_agent_tools/core/job-service.js +112 -0
  20. package/dist/hoi4_agent_tools/core/job-store.js +641 -0
  21. package/dist/hoi4_agent_tools/core/job-transactions.js +126 -0
  22. package/dist/hoi4_agent_tools/core/job-worker-host.js +200 -0
  23. package/dist/hoi4_agent_tools/core/job-worker.js +62 -0
  24. package/dist/hoi4_agent_tools/core/operation-context.js +31 -0
  25. package/dist/hoi4_agent_tools/core/operation-result.js +412 -0
  26. package/dist/hoi4_agent_tools/core/operation-tasks.js +382 -0
  27. package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +383 -0
  28. package/dist/hoi4_agent_tools/core/result.js +1 -0
  29. package/dist/hoi4_agent_tools/core/scanner.js +15 -75
  30. package/dist/hoi4_agent_tools/core/scenario-state.js +97 -0
  31. package/dist/hoi4_agent_tools/core/semantic-dependencies.js +233 -0
  32. package/dist/hoi4_agent_tools/core/shared-request-capacity.js +74 -25
  33. package/dist/hoi4_agent_tools/core/source/cache.js +107 -0
  34. package/dist/hoi4_agent_tools/core/source/parser.js +11 -1
  35. package/dist/hoi4_agent_tools/core/transaction-execution.js +134 -0
  36. package/dist/hoi4_agent_tools/core/transactions.js +80 -27
  37. package/dist/hoi4_agent_tools/event/graph.js +214 -290
  38. package/dist/hoi4_agent_tools/event/helper-expansion.js +33 -0
  39. package/dist/hoi4_agent_tools/event/job-operations.js +51 -0
  40. package/dist/hoi4_agent_tools/event/operations.js +179 -0
  41. package/dist/hoi4_agent_tools/event/service.js +67 -15
  42. package/dist/hoi4_agent_tools/event/source-analysis.js +5 -4
  43. package/dist/hoi4_agent_tools/focus/job-operations.js +77 -0
  44. package/dist/hoi4_agent_tools/focus/tool-operations.js +669 -0
  45. package/dist/hoi4_agent_tools/focus/tool-support.js +123 -0
  46. package/dist/hoi4_agent_tools/gui/job-operations.js +81 -0
  47. package/dist/hoi4_agent_tools/gui/scenario-conditions.js +119 -0
  48. package/dist/hoi4_agent_tools/gui/scenario-generator.js +59 -73
  49. package/dist/hoi4_agent_tools/gui/scenario.js +3 -0
  50. package/dist/hoi4_agent_tools/gui/studio.js +5 -2
  51. package/dist/hoi4_agent_tools/gui/tool-operations.js +270 -0
  52. package/dist/hoi4_agent_tools/map/job-operations.js +76 -0
  53. package/dist/hoi4_agent_tools/map/tool-operations.js +493 -0
  54. package/dist/hoi4_agent_tools/mcp/prompts/probability.js +25 -0
  55. package/dist/hoi4_agent_tools/mcp/prompts/register-modern.js +29 -0
  56. package/dist/hoi4_agent_tools/mcp/resources/register-modern.js +34 -0
  57. package/dist/hoi4_agent_tools/mcp/resources/register.js +4 -150
  58. package/dist/hoi4_agent_tools/mcp/server/base-tools.js +3 -219
  59. package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +188 -0
  60. package/dist/hoi4_agent_tools/mcp/server/create.js +16 -6
  61. package/dist/hoi4_agent_tools/mcp/server/instructions.js +2 -0
  62. package/dist/hoi4_agent_tools/mcp/server/job-task-store.js +67 -0
  63. package/dist/hoi4_agent_tools/mcp/server/legacy-task-tools.js +20 -0
  64. package/dist/hoi4_agent_tools/mcp/server/modern-tasks.js +168 -0
  65. package/dist/hoi4_agent_tools/mcp/server/progress.js +17 -15
  66. package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +43 -0
  67. package/dist/hoi4_agent_tools/mcp/server/result.js +2 -412
  68. package/dist/hoi4_agent_tools/mcp/server/task-request-context.js +9 -0
  69. package/dist/hoi4_agent_tools/mcp/server/task-tool-catalog.js +14 -0
  70. package/dist/hoi4_agent_tools/mcp/server/task-tool-definition.js +1 -0
  71. package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +1 -123
  72. package/dist/hoi4_agent_tools/mcp/tools/chaosx.js +38 -21
  73. package/dist/hoi4_agent_tools/mcp/tools/event.js +24 -315
  74. package/dist/hoi4_agent_tools/mcp/tools/focus.js +34 -1039
  75. package/dist/hoi4_agent_tools/mcp/tools/gui.js +19 -504
  76. package/dist/hoi4_agent_tools/mcp/tools/job.js +46 -0
  77. package/dist/hoi4_agent_tools/mcp/tools/map.js +21 -600
  78. package/dist/hoi4_agent_tools/mcp/tools/probability.js +39 -463
  79. package/dist/hoi4_agent_tools/mcp/tools/technology.js +40 -304
  80. package/dist/hoi4_agent_tools/mcp/transports/http.js +58 -1
  81. package/dist/hoi4_agent_tools/mcp/transports/modern-task-routing.js +109 -0
  82. package/dist/hoi4_agent_tools/mcp/transports/negotiated-stdio.js +121 -0
  83. package/dist/hoi4_agent_tools/probability/job-operations.js +58 -0
  84. package/dist/hoi4_agent_tools/probability/operations.js +183 -0
  85. package/dist/hoi4_agent_tools/probability/scenario-state.js +2 -95
  86. package/dist/hoi4_agent_tools/probability/service.js +24 -3
  87. package/dist/hoi4_agent_tools/probability/trigger-evaluator.js +1 -287
  88. package/dist/hoi4_agent_tools/schemas/common.js +2 -0
  89. package/dist/hoi4_agent_tools/schemas/event.js +72 -1
  90. package/dist/hoi4_agent_tools/schemas/focus-requests.js +189 -0
  91. package/dist/hoi4_agent_tools/schemas/gui-requests.js +193 -0
  92. package/dist/hoi4_agent_tools/schemas/helper-expansion.js +58 -0
  93. package/dist/hoi4_agent_tools/schemas/map-requests.js +85 -0
  94. package/dist/hoi4_agent_tools/schemas/probability-requests.js +168 -0
  95. package/dist/hoi4_agent_tools/schemas/technology.js +90 -1
  96. package/dist/hoi4_agent_tools/schemas/transaction.js +1 -0
  97. package/dist/hoi4_agent_tools/technology/graph.js +83 -78
  98. package/dist/hoi4_agent_tools/technology/helper-expansion.js +33 -0
  99. package/dist/hoi4_agent_tools/technology/job-operations.js +60 -0
  100. package/dist/hoi4_agent_tools/technology/operations.js +125 -0
  101. package/dist/hoi4_agent_tools/technology/service.js +89 -15
  102. package/dist/hoi4_agent_tools/technology/source-analysis.js +2 -1
  103. package/dist/hoi4_agent_tools/version.js +1 -1
  104. package/docs/README.md +2 -0
  105. package/docs/development.md +2 -0
  106. package/docs/events.md +7 -2
  107. package/docs/gui.md +3 -1
  108. package/docs/helper-expansion.md +78 -0
  109. package/docs/http.md +2 -0
  110. package/docs/jobs.md +85 -0
  111. package/docs/technology.md +6 -2
  112. package/package.json +11 -5
  113. package/schemas/configuration.schema.json +1 -1
  114. package/schemas/continuous-focus-palette.schema.json +1 -1
  115. package/schemas/custom-weighted-pool.schema.json +1 -1
  116. package/schemas/event-feature-manifest.schema.json +1 -1
  117. package/schemas/focus-plan.schema.json +1 -1
  118. package/schemas/focus-planning-sidecar.schema.json +1 -1
  119. package/schemas/gui-animation-source.schema.json +1 -1
  120. package/schemas/gui-helper.schema.json +1 -1
  121. package/schemas/gui-scenario.schema.json +189 -33
  122. package/schemas/helper-expansion-request.schema.json +44 -0
  123. package/schemas/helper-expansion-summary.schema.json +70 -0
  124. package/schemas/map-operation.schema.json +1 -1
  125. package/schemas/operation-result.schema.json +1 -1
  126. package/schemas/probability-analysis-result.schema.json +1 -1
  127. package/schemas/probability-compare-input.schema.json +1 -1
  128. package/schemas/probability-evaluate-input.schema.json +1 -1
  129. package/schemas/probability-inspect-input.schema.json +1 -1
  130. package/schemas/probability-render-input.schema.json +1 -1
  131. package/schemas/probability-scenario-set.schema.json +1 -1
  132. package/schemas/probability-sequence-input.schema.json +1 -1
  133. package/schemas/probability-simulate-input.schema.json +1 -1
  134. package/schemas/probability-sweep-input.schema.json +1 -1
  135. package/server.json +2 -2
@@ -0,0 +1,270 @@
1
+ import { z } from 'zod/v4';
2
+ import { boundedSourceHashEvidence, publicArtifactLink } from '../core/artifacts.js';
3
+ import { canonicalJson, compareCodeUnits, hashCanonical } from '../core/canonical.js';
4
+ import { autonomousResultArtifacts, } from '../core/transaction-execution.js';
5
+ import { emptyServiceResult } from '../core/result.js';
6
+ import { setInlineFilesScanned } from '../core/operation-result.js';
7
+ import { PACKAGE_VERSION } from '../version.js';
8
+ import { compileGuiHelpers } from './helpers.js';
9
+ import { encodeGuiInspectionArtifact, projectGuiGraphForArtifact } from './inspection-artifact.js';
10
+ const guiRewriteRecipeSchema = z
11
+ .object({
12
+ mode: z.enum(['source', 'helpers', 'patches']),
13
+ filesScanned: z.array(z.string().min(1).max(4096)),
14
+ compilation: z
15
+ .object({
16
+ nodeCount: z.number().int().nonnegative(),
17
+ templateInstanceCount: z.number().int().nonnegative(),
18
+ rawEscapeCount: z.number().int().nonnegative(),
19
+ })
20
+ .strict()
21
+ .optional(),
22
+ })
23
+ .strict();
24
+ function validationSummary(validation) {
25
+ return {
26
+ passed: validation.checks.every(({ passed }) => passed) &&
27
+ !validation.diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker'),
28
+ checks: validation.checks,
29
+ };
30
+ }
31
+ export function normalizeGuiInspectRequest(input) {
32
+ return input;
33
+ }
34
+ export function normalizeGuiRenderRequest(input) {
35
+ return input;
36
+ }
37
+ export function normalizeGuiRewriteRequest(input) {
38
+ return input;
39
+ }
40
+ export async function prepareGuiRewrite(studio, input, context) {
41
+ await context.progress(0, 3, 'Validating the requested GUI result');
42
+ const compilation = input.mode === 'helpers' ? compileGuiHelpers(input.helper) : undefined;
43
+ const transaction = await studio.planSource({
44
+ workspaceId: context.workspaceId,
45
+ relativePath: input.relativePath,
46
+ ...(input.mode === 'patches'
47
+ ? { expectedSourceHash: input.expectedSourceHash, patches: input.patches }
48
+ : {
49
+ source: input.mode === 'source' ? input.source : compilation.source,
50
+ ...(input.additionalFiles === undefined
51
+ ? {}
52
+ : { additionalFiles: input.additionalFiles }),
53
+ }),
54
+ windowName: input.windowName,
55
+ scenario: input.scenario,
56
+ ...(context.principal === undefined ? {} : { principal: context.principal }),
57
+ ...(context.signal === undefined ? {} : { signal: context.signal }),
58
+ });
59
+ await context.progress(2, 3, 'GUI rewrite prepared for journaled application');
60
+ return {
61
+ transaction,
62
+ recipe: guiRewriteRecipeSchema.parse({
63
+ mode: input.mode,
64
+ filesScanned: [
65
+ input.relativePath,
66
+ ...(input.additionalFiles ?? []).map(({ relativePath }) => relativePath),
67
+ ].sort(compareCodeUnits),
68
+ ...(compilation === undefined
69
+ ? {}
70
+ : {
71
+ compilation: {
72
+ nodeCount: compilation.nodeCount,
73
+ templateInstanceCount: compilation.templateInstanceCount,
74
+ rawEscapeCount: compilation.rawEscapeCount,
75
+ },
76
+ }),
77
+ }),
78
+ };
79
+ }
80
+ export function completeGuiRewrite(workspaceId, execution, recipe) {
81
+ const parsed = guiRewriteRecipeSchema.parse(recipe);
82
+ const transaction = execution.transaction;
83
+ const result = emptyServiceResult(workspaceId, {
84
+ mode: parsed.mode,
85
+ execution: execution.outcome,
86
+ fileCount: transaction.files.length,
87
+ artifactCount: transaction.artifacts.length,
88
+ ...(parsed.compilation ?? {}),
89
+ });
90
+ result.status = transaction.validation.passed ? 'ok' : 'blocked';
91
+ result.code =
92
+ execution.outcome === 'applied'
93
+ ? 'GUI_CHANGES_APPLIED'
94
+ : execution.outcome === 'unchanged'
95
+ ? 'GUI_CHANGES_UNCHANGED'
96
+ : 'GUI_CHANGES_BLOCKED';
97
+ setInlineFilesScanned(result, parsed.filesScanned);
98
+ result.proposedFiles = transaction.files.slice(0, 100).map(({ relativePath }) => relativePath);
99
+ result.changedFiles = transaction.appliedFiles.slice(0, 100);
100
+ result.diagnostics = transaction.diagnostics.slice(0, 100);
101
+ result.artifacts = autonomousResultArtifacts(execution);
102
+ result.validation = transaction.validation;
103
+ result.blockers = transaction.diagnostics
104
+ .filter(({ severity }) => severity === 'error' || severity === 'blocker')
105
+ .map(({ code, message, details }) => ({
106
+ code,
107
+ message,
108
+ ...(details === undefined ? {} : { details }),
109
+ }));
110
+ return result;
111
+ }
112
+ export async function inspectGui(engine, studio, input, context) {
113
+ await context.progress(0, 3, 'Building GUI source graph');
114
+ const inspected = input.windowName === undefined || input.scenario === undefined
115
+ ? {
116
+ graph: (await studio.scan(context.workspaceId, context.principal, context.signal)).graph,
117
+ linted: undefined,
118
+ }
119
+ : await (async () => {
120
+ const linted = await studio.lint({
121
+ workspaceId: context.workspaceId,
122
+ windowName: input.windowName,
123
+ scenario: input.scenario,
124
+ ...(input.relatedScenarios === undefined
125
+ ? {}
126
+ : { relatedScenarios: input.relatedScenarios }),
127
+ generatedScenarios: input.generatedScenarios ?? {},
128
+ ...(context.principal === undefined ? {} : { principal: context.principal }),
129
+ ...(context.signal === undefined ? {} : { signal: context.signal }),
130
+ });
131
+ return { graph: linted.graph, linted };
132
+ })();
133
+ const { graph, linted } = inspected;
134
+ const sharedRevision = hashCanonical(graph.sourceHashes);
135
+ const workspace = engine.resolver.get(context.workspaceId, context.principal);
136
+ const sourceEvidence = boundedSourceHashEvidence(graph.sourceHashes);
137
+ const artifactGraph = projectGuiGraphForArtifact(graph, linted?.scene.elements.map(({ sourceId }) => sourceId));
138
+ const inspectionName = `gui-inspect.${sharedRevision.slice(0, 16)}.json`;
139
+ const inspectionJson = `${canonicalJson({
140
+ schemaVersion: 1,
141
+ sharedRevision,
142
+ offline: true,
143
+ graph: artifactGraph.graph,
144
+ ...(artifactGraph.projection === undefined
145
+ ? {}
146
+ : { graphProjection: artifactGraph.projection }),
147
+ ...(linted === undefined
148
+ ? {}
149
+ : {
150
+ scenario: linted.scene.scenario,
151
+ fidelity: linted.scene.fidelity,
152
+ validation: linted.validation,
153
+ }),
154
+ })}\n`;
155
+ const encodedInspection = await encodeGuiInspectionArtifact(inspectionName, inspectionJson);
156
+ const artifact = await engine.artifacts.putChunked(workspace, encodedInspection.name, encodedInspection.mimeType, encodedInspection.content, {
157
+ kind: 'gui-inspect',
158
+ toolVersion: PACKAGE_VERSION,
159
+ schemaVersion: 'gui-inspect.v1',
160
+ sourceHashes: sourceEvidence.sourceHashes,
161
+ metadata: {
162
+ sharedRevision,
163
+ offline: true,
164
+ complete: graph.complete,
165
+ skippedSourceCount: graph.skippedSourceCount,
166
+ sourceHashInventory: sourceEvidence.inventory,
167
+ compressed: encodedInspection.compressed,
168
+ uncompressedBytes: encodedInspection.uncompressedBytes,
169
+ graphProjection: artifactGraph.projection?.mode ?? 'full',
170
+ },
171
+ }, encodedInspection.compressed
172
+ ? 'Gzip-compressed scripted GUI graph, diagnostics, and optional scenario fidelity inspection'
173
+ : 'Scripted GUI graph, diagnostics, and optional scenario fidelity inspection', context.signal);
174
+ const diagnostics = linted === undefined ? graph.diagnostics : linted.validation.diagnostics;
175
+ const result = emptyServiceResult(context.workspaceId, {
176
+ sharedRevision,
177
+ complete: graph.complete,
178
+ skippedSourceCount: graph.skippedSourceCount,
179
+ skippedSources: graph.skippedSources,
180
+ nodes: graph.nodes.length,
181
+ edges: graph.edges.length,
182
+ elements: graph.elements.length,
183
+ sprites: graph.sprites.length,
184
+ fonts: graph.fonts.length,
185
+ scriptedGuis: graph.scriptedGuis.length,
186
+ ...(linted === undefined
187
+ ? {}
188
+ : {
189
+ windowName: input.windowName,
190
+ scenarioId: linted.scene.scenario.id,
191
+ inspectedElementCount: linted.scene.elements.length,
192
+ fidelityCounts: Object.fromEntries(Object.entries(linted.scene.fidelity).map(([key, values]) => [key, values.length])),
193
+ }),
194
+ });
195
+ result.code = graph.complete ? 'GUI_INSPECTED' : 'GUI_INSPECTED_PARTIAL';
196
+ setInlineFilesScanned(result, graph.filesScanned);
197
+ result.diagnostics = diagnostics.slice(0, 100);
198
+ result.artifacts = [publicArtifactLink(artifact)];
199
+ result.validation =
200
+ linted === undefined
201
+ ? {
202
+ passed: !diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker'),
203
+ checks: [
204
+ {
205
+ id: 'gui-inspection',
206
+ passed: true,
207
+ message: graph.complete
208
+ ? `${graph.nodes.length} GUI graph nodes connected from a complete inventory`
209
+ : `${graph.nodes.length} GUI graph nodes connected; ${graph.skippedSourceCount} over-limit source(s) were skipped`,
210
+ },
211
+ ],
212
+ }
213
+ : validationSummary({ diagnostics, checks: linted.validation.checks });
214
+ await context.progress(3, 3, 'GUI inspection complete');
215
+ return result;
216
+ }
217
+ export async function renderGui(studio, input, context) {
218
+ await context.progress(0, 4, 'Building GUI source graph');
219
+ const rendered = await studio.renderAndStore({
220
+ workspaceId: context.workspaceId,
221
+ windowName: input.windowName,
222
+ scenario: input.scenario,
223
+ ...(input.states === undefined ? {} : { states: input.states }),
224
+ ...(input.resolutions === undefined
225
+ ? {}
226
+ : {
227
+ resolutions: input.resolutions.map(({ width, height, uiScale }) => ({
228
+ width,
229
+ height,
230
+ ...(uiScale === undefined ? {} : { uiScale }),
231
+ })),
232
+ }),
233
+ ...(input.relatedScenarios === undefined ? {} : { relatedScenarios: input.relatedScenarios }),
234
+ generatedScenarios: input.generatedScenarios ?? {},
235
+ ...(input.comparisonScenario === undefined
236
+ ? {}
237
+ : { comparisonScenario: input.comparisonScenario }),
238
+ ...(context.principal === undefined ? {} : { principal: context.principal }),
239
+ ...(context.signal === undefined ? {} : { signal: context.signal }),
240
+ });
241
+ await context.progress(3, 4, 'Preparing GUI result');
242
+ const diagnostics = rendered.validation.diagnostics;
243
+ const result = emptyServiceResult(context.workspaceId, {
244
+ windowName: input.windowName,
245
+ scenarioId: rendered.render.scene.scenario.id,
246
+ sourceRevision: rendered.render.scene.sourceRevision,
247
+ variantCount: rendered.render.images.length,
248
+ variants: rendered.render.images.slice(0, 64).map(({ variant, width, height }) => ({
249
+ variant,
250
+ width,
251
+ height,
252
+ })),
253
+ stateCount: rendered.stateScenes.length,
254
+ scenarioCount: rendered.scenarioScenes.length,
255
+ resolutionCount: rendered.resolutionScenes.length,
256
+ comparison: {
257
+ changedPixels: rendered.comparison.changedPixels,
258
+ changedRatio: rendered.comparison.changedRatio,
259
+ },
260
+ fidelityCounts: Object.fromEntries(Object.entries(rendered.render.fidelity).map(([key, values]) => [key, values.length])),
261
+ offlineRepresentation: true,
262
+ });
263
+ result.code = 'GUI_RENDERED';
264
+ setInlineFilesScanned(result, rendered.filesScanned);
265
+ result.diagnostics = diagnostics.slice(0, 100);
266
+ result.artifacts = rendered.artifacts.map(publicArtifactLink);
267
+ result.validation = validationSummary({ diagnostics, checks: rendered.validation.checks });
268
+ await context.progress(4, 4, 'GUI render complete');
269
+ return result;
270
+ }
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod/v4';
2
+ import { IdleCacheLifetime } from '../core/idle-cache-lifetime.js';
3
+ import { errorResult, toolResult } from '../core/operation-result.js';
4
+ import { ServiceError } from '../core/result.js';
5
+ import { mapInspectRequestSchema, mapRenderRequestSchema, mapRewriteRequestSchema, } from '../schemas/map-requests.js';
6
+ import { AgentNudger } from './service.js';
7
+ import { inspectMap, completeMapRewrite, normalizeMapInspectRequest, normalizeMapRenderRequest, normalizeMapRewriteRequest, prepareMapRewrite, renderMapView, } from './tool-operations.js';
8
+ const wireResult = z.record(z.string(), z.json());
9
+ /** Registers read-only map operations without invoking transport handlers. */
10
+ export function registerMapJobs(operations, engine) {
11
+ const nudger = new AgentNudger(engine);
12
+ const lifetime = new IdleCacheLifetime(() => engine.releaseScanCaches());
13
+ function register(name, schema, normalize, execute) {
14
+ operations.registerRead(name, schema, async (input, context) => {
15
+ const release = lifetime.begin();
16
+ try {
17
+ if (input.workspaceId !== 'current' && input.workspaceId !== context.workspaceId)
18
+ throw new ServiceError('JOB_ARGUMENT_SCOPE_MISMATCH', 'The request workspace does not match the authorized job workspace');
19
+ const result = await execute(nudger, normalize(input), {
20
+ workspaceId: context.workspaceId,
21
+ ...(context.principal === undefined ? {} : { principal: context.principal }),
22
+ signal: context.signal,
23
+ progress: (completed, total, message) => context.progress({ completed, total, message }),
24
+ });
25
+ const wire = wireResult.parse(JSON.parse(JSON.stringify(toolResult(result))));
26
+ const revision = result.data !== null &&
27
+ typeof result.data === 'object' &&
28
+ 'revision' in result.data &&
29
+ typeof result.data.revision === 'string'
30
+ ? result.data.revision
31
+ : undefined;
32
+ if (revision !== undefined)
33
+ await context.stageResult(wire, { sourceRevision: revision });
34
+ return wire;
35
+ }
36
+ catch (error) {
37
+ if (context.signal.aborted)
38
+ throw error;
39
+ return wireResult.parse(JSON.parse(JSON.stringify(errorResult(error, context.workspaceId))));
40
+ }
41
+ finally {
42
+ release();
43
+ }
44
+ });
45
+ }
46
+ register('hoi4.map_inspect', mapInspectRequestSchema, normalizeMapInspectRequest, (nudger, input, context) => inspectMap(engine, nudger, input, context));
47
+ register('hoi4.map_render', mapRenderRequestSchema, normalizeMapRenderRequest, renderMapView);
48
+ operations.registerWrite('hoi4.map_rewrite', mapRewriteRequestSchema, async (input, context) => {
49
+ if (input.workspaceId !== 'current' && input.workspaceId !== context.workspaceId)
50
+ throw new ServiceError('JOB_ARGUMENT_SCOPE_MISMATCH', 'The request workspace does not match the authorized job workspace');
51
+ const release = lifetime.begin();
52
+ try {
53
+ return await prepareMapRewrite(nudger, normalizeMapRewriteRequest(input), {
54
+ workspaceId: context.workspaceId,
55
+ ...(context.principal === undefined ? {} : { principal: context.principal }),
56
+ signal: context.signal,
57
+ progress: (completed, total, message) => context.progress({ completed, total, message }),
58
+ });
59
+ }
60
+ finally {
61
+ release();
62
+ }
63
+ }, async (_input, execution, _reconciled, recipe, context) => {
64
+ const result = completeMapRewrite(context.workspaceId, execution, recipe);
65
+ await context.progress({
66
+ completed: 5,
67
+ total: 5,
68
+ message: execution.outcome === 'applied'
69
+ ? 'Map rewrite complete'
70
+ : execution.outcome === 'unchanged'
71
+ ? 'Map content already satisfied the operations'
72
+ : 'Map rewrite blocked',
73
+ });
74
+ return wireResult.parse(JSON.parse(JSON.stringify(toolResult(result))));
75
+ });
76
+ }