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
@@ -0,0 +1,189 @@
1
+ import { z } from 'zod/v4';
2
+ import { FOCUS_RENDER_MAX_OUTPUT_SCALE, FOCUS_RENDER_MIN_OUTPUT_SCALE } from '../focus/index.js';
3
+ import { backgroundRequestKeySchema, workspaceIdSchema, workspaceRelativePathSchema, } from './common.js';
4
+ import { continuousFocusPaletteSchema, focusLayoutSchema, focusTreePlanSchema } from './focus.js';
5
+ function compact(schema, description) {
6
+ return z.unknown().describe(description).pipe(schema);
7
+ }
8
+ const compactFocusLayoutSchema = compact(focusLayoutSchema, 'Prior deterministic focus layout used to stabilize unchanged positions.');
9
+ const compactFocusPlanSchema = compact(z.union([focusTreePlanSchema, continuousFocusPaletteSchema]), 'Complete national or continuous focus plan.');
10
+ export const focusInspectRequestSchema = z
11
+ .object({
12
+ mode: z.enum(['national', 'continuous']).optional(),
13
+ workspaceId: workspaceIdSchema,
14
+ relativePath: workspaceRelativePathSchema.optional(),
15
+ treeId: z.string().min(1).max(256).optional(),
16
+ paletteId: z.string().min(1).max(256).optional(),
17
+ previous: compactFocusLayoutSchema.optional(),
18
+ laneSpacing: z.number().int().min(1).max(100).optional(),
19
+ nodeSpacing: z.number().int().min(1).max(100).optional(),
20
+ })
21
+ .strict()
22
+ .superRefine((value, context) => {
23
+ const mode = value.mode ?? 'national';
24
+ if (mode === 'national' && value.paletteId !== undefined)
25
+ context.addIssue({
26
+ code: 'custom',
27
+ path: ['paletteId'],
28
+ message: 'paletteId is only valid in continuous mode',
29
+ });
30
+ if (mode === 'continuous' && value.treeId !== undefined)
31
+ context.addIssue({
32
+ code: 'custom',
33
+ path: ['treeId'],
34
+ message: 'treeId is only valid in national mode',
35
+ });
36
+ if (mode === 'continuous') {
37
+ for (const field of ['previous', 'laneSpacing', 'nodeSpacing']) {
38
+ if (value[field] !== undefined)
39
+ context.addIssue({
40
+ code: 'custom',
41
+ path: [field],
42
+ message: `${field} is only valid in national mode`,
43
+ });
44
+ }
45
+ }
46
+ if (value.previous !== undefined && value.treeId === undefined)
47
+ context.addIssue({
48
+ code: 'custom',
49
+ path: ['previous'],
50
+ message: 'previous requires treeId so it applies to exactly one tree',
51
+ });
52
+ });
53
+ export const focusRenderRequestSchema = z
54
+ .object({
55
+ mode: z.enum(['national', 'continuous']).optional(),
56
+ workspaceId: workspaceIdSchema,
57
+ relativePath: workspaceRelativePathSchema,
58
+ treeId: z.string().min(1).max(256).optional(),
59
+ paletteId: z.string().min(1).max(256).optional(),
60
+ horizontalSpacing: z.number().int().min(80).max(1000).optional(),
61
+ verticalSpacing: z.number().int().min(60).max(1000).optional(),
62
+ reviewScale: z
63
+ .number()
64
+ .min(FOCUS_RENDER_MIN_OUTPUT_SCALE)
65
+ .max(FOCUS_RENDER_MAX_OUTPUT_SCALE)
66
+ .optional(),
67
+ columns: z.number().int().min(1).max(12).optional(),
68
+ padding: z.number().int().min(0).max(1000).optional(),
69
+ })
70
+ .strict()
71
+ .superRefine((value, context) => {
72
+ const mode = value.mode ?? 'national';
73
+ const invalid = mode === 'national'
74
+ ? [
75
+ ['paletteId', value.paletteId],
76
+ ['columns', value.columns],
77
+ ]
78
+ : [
79
+ ['treeId', value.treeId],
80
+ ['horizontalSpacing', value.horizontalSpacing],
81
+ ['verticalSpacing', value.verticalSpacing],
82
+ ['reviewScale', value.reviewScale],
83
+ ];
84
+ for (const [field, fieldValue] of invalid) {
85
+ if (fieldValue !== undefined)
86
+ context.addIssue({
87
+ code: 'custom',
88
+ path: [field],
89
+ message: `${field} is not valid in ${mode} mode`,
90
+ });
91
+ }
92
+ if (mode === 'continuous' && value.padding !== undefined && value.padding < 24)
93
+ context.addIssue({
94
+ code: 'custom',
95
+ path: ['padding'],
96
+ message: 'continuous focus padding must be at least 24 pixels',
97
+ });
98
+ });
99
+ export const focusRewriteRequestSchema = z
100
+ .object({
101
+ mode: z.enum(['national', 'continuous']).optional(),
102
+ workspaceId: workspaceIdSchema,
103
+ relativePath: workspaceRelativePathSchema,
104
+ treeId: z.string().min(1).max(256).optional(),
105
+ layoutMode: z.enum(['authored', 'compact']).default('authored'),
106
+ plan: compactFocusPlanSchema.optional(),
107
+ createIfMissing: z.boolean().default(false),
108
+ horizontalSpacing: z.number().int().min(80).max(1000).optional(),
109
+ verticalSpacing: z.number().int().min(60).max(1000).optional(),
110
+ padding: z.number().int().min(0).max(1000).optional(),
111
+ reviewScale: z
112
+ .number()
113
+ .min(FOCUS_RENDER_MIN_OUTPUT_SCALE)
114
+ .max(FOCUS_RENDER_MAX_OUTPUT_SCALE)
115
+ .optional(),
116
+ requestKey: backgroundRequestKeySchema.optional(),
117
+ })
118
+ .strict()
119
+ .superRefine((value, context) => {
120
+ const mode = value.mode ?? 'national';
121
+ const schema = mode === 'continuous' ? continuousFocusPaletteSchema : focusTreePlanSchema;
122
+ if (value.plan !== undefined && !schema.safeParse(value.plan).success)
123
+ context.addIssue({
124
+ code: 'custom',
125
+ path: ['plan'],
126
+ message: `plan must be a ${mode} focus plan`,
127
+ });
128
+ if (mode === 'continuous') {
129
+ if (value.plan === undefined)
130
+ context.addIssue({
131
+ code: 'custom',
132
+ path: ['plan'],
133
+ message: 'plan is required in continuous mode',
134
+ });
135
+ if (value.treeId !== undefined)
136
+ context.addIssue({
137
+ code: 'custom',
138
+ path: ['treeId'],
139
+ message: 'treeId is only valid in national mode',
140
+ });
141
+ if (value.layoutMode !== 'authored')
142
+ context.addIssue({
143
+ code: 'custom',
144
+ path: ['layoutMode'],
145
+ message: 'compact layout mode is only valid in national mode',
146
+ });
147
+ for (const [field, fieldValue] of [
148
+ ['horizontalSpacing', value.horizontalSpacing],
149
+ ['verticalSpacing', value.verticalSpacing],
150
+ ['padding', value.padding],
151
+ ['reviewScale', value.reviewScale],
152
+ ])
153
+ if (fieldValue !== undefined)
154
+ context.addIssue({
155
+ code: 'custom',
156
+ path: [field],
157
+ message: `${field} is only valid for national focus review renders`,
158
+ });
159
+ }
160
+ else {
161
+ if (value.plan === undefined && value.layoutMode !== 'compact')
162
+ context.addIssue({
163
+ code: 'custom',
164
+ path: ['plan'],
165
+ message: 'plan is required unless layoutMode is compact',
166
+ });
167
+ if (value.plan === undefined && value.treeId === undefined)
168
+ context.addIssue({
169
+ code: 'custom',
170
+ path: ['treeId'],
171
+ message: 'treeId is required for plan-free compact reflow',
172
+ });
173
+ if (value.plan === undefined && value.createIfMissing)
174
+ context.addIssue({
175
+ code: 'custom',
176
+ path: ['createIfMissing'],
177
+ message: 'plan-free compact reflow requires an existing focus source',
178
+ });
179
+ if (value.plan !== undefined &&
180
+ value.treeId !== undefined &&
181
+ 'id' in value.plan &&
182
+ value.plan.id !== value.treeId)
183
+ context.addIssue({
184
+ code: 'custom',
185
+ path: ['treeId'],
186
+ message: 'treeId must match the supplied national focus plan',
187
+ });
188
+ }
189
+ });
@@ -0,0 +1,193 @@
1
+ import { z } from 'zod/v4';
2
+ import { renderDimensionViolation, RenderBudget, RENDER_MAX_DIMENSION, } from '../core/render-budget.js';
3
+ import { ServiceError } from '../core/result.js';
4
+ import { SOURCE_MAX_BYTES } from '../core/source/index.js';
5
+ import { GUI_TEXT_PACKAGE_MAX_FILES, GuiHelperDocumentSchema } from '../gui/index.js';
6
+ import { GuiGeneratedScenarioOptionsSchema, GuiPreviewScenarioSchema } from '../gui/scenario.js';
7
+ import { backgroundRequestKeySchema, workspaceIdSchema, workspaceRelativePathSchema, } from './common.js';
8
+ function compact(schema, description) {
9
+ return z.unknown().describe(description).pipe(schema);
10
+ }
11
+ const compactGuiScenarioSchema = compact(GuiPreviewScenarioSchema, 'GUI preview scenario.');
12
+ const compactGeneratedScenarioOptionsSchema = compact(GuiGeneratedScenarioOptionsSchema, 'GUI generated-scenario options.');
13
+ const compactGuiHelperSchema = compact(GuiHelperDocumentSchema, 'GUI helper document.');
14
+ export const guiPreviewStateSchema = z.enum([
15
+ 'normal',
16
+ 'hover',
17
+ 'selected',
18
+ 'locked',
19
+ 'disabled',
20
+ 'warning',
21
+ 'active',
22
+ 'completed',
23
+ 'empty-list',
24
+ 'full-list',
25
+ 'minimum-value',
26
+ 'maximum-value',
27
+ 'long-text',
28
+ 'missing-localisation',
29
+ ]);
30
+ export const guiInspectRequestSchema = z
31
+ .object({
32
+ workspaceId: workspaceIdSchema,
33
+ windowName: z.string().min(1).max(256).optional(),
34
+ scenario: compactGuiScenarioSchema.optional(),
35
+ relatedScenarios: z.array(compactGuiScenarioSchema).max(32).optional(),
36
+ generatedScenarios: compactGeneratedScenarioOptionsSchema.optional(),
37
+ })
38
+ .strict()
39
+ .superRefine((value, context) => {
40
+ if ((value.windowName === undefined) !== (value.scenario === undefined))
41
+ context.addIssue({
42
+ code: 'custom',
43
+ message: 'windowName and scenario must be provided together',
44
+ });
45
+ if (value.relatedScenarios !== undefined && value.scenario === undefined)
46
+ context.addIssue({
47
+ code: 'custom',
48
+ path: ['relatedScenarios'],
49
+ message: 'relatedScenarios requires a window scenario',
50
+ });
51
+ if (value.generatedScenarios !== undefined && value.scenario === undefined)
52
+ context.addIssue({
53
+ code: 'custom',
54
+ path: ['generatedScenarios'],
55
+ message: 'generatedScenarios requires a window scenario',
56
+ });
57
+ });
58
+ export const guiResolutionSchema = z
59
+ .object({
60
+ width: z.number().int().min(320).max(RENDER_MAX_DIMENSION),
61
+ height: z.number().int().min(200).max(RENDER_MAX_DIMENSION),
62
+ uiScale: z
63
+ .number()
64
+ .min(0.25)
65
+ .max(4)
66
+ .optional()
67
+ .describe('In-game UI scale for this resolution. Use 1 at 1920x1080; do not use it to enlarge an artifact.'),
68
+ })
69
+ .strict()
70
+ .superRefine(({ width, height }, context) => {
71
+ const violation = renderDimensionViolation(width, height, 'GUI matrix resolution');
72
+ if (violation !== undefined)
73
+ context.addIssue({ code: 'custom', message: `${violation.code}: ${violation.message}` });
74
+ });
75
+ export const guiRenderRequestSchema = z
76
+ .object({
77
+ workspaceId: workspaceIdSchema,
78
+ windowName: z.string().min(1).max(256),
79
+ scenario: compactGuiScenarioSchema,
80
+ states: z.array(guiPreviewStateSchema).max(14).optional(),
81
+ resolutions: z.array(guiResolutionSchema).min(1).max(16).optional(),
82
+ relatedScenarios: z.array(compactGuiScenarioSchema).max(32).optional(),
83
+ generatedScenarios: compactGeneratedScenarioOptionsSchema.optional(),
84
+ comparisonScenario: compactGuiScenarioSchema.optional(),
85
+ })
86
+ .strict()
87
+ .superRefine(({ scenario, states, resolutions, relatedScenarios, generatedScenarios }, context) => {
88
+ const budget = new RenderBudget();
89
+ try {
90
+ const stateCount = states?.length ?? 14;
91
+ for (let index = 0; index < 9 + stateCount; index += 1)
92
+ budget.reserve(scenario.resolution.width, scenario.resolution.height, 'GUI request variant');
93
+ for (const resolution of resolutions ?? [
94
+ { width: 1280, height: 720 },
95
+ { width: 1920, height: 1080 },
96
+ { width: 2560, height: 1440 },
97
+ { width: 1920, height: 1080 },
98
+ ])
99
+ budget.reserve(resolution.width, resolution.height, 'GUI resolution variant');
100
+ const generatedCount = generatedScenarios?.enabled === false ? 0 : (generatedScenarios?.count ?? 1);
101
+ const scenarioCount = generatedCount === 0
102
+ ? 1 + (relatedScenarios?.length ?? 0)
103
+ : generatedCount +
104
+ (generatedScenarios?.preservePlaceholder === false ? 0 : 1) +
105
+ (relatedScenarios?.length ?? 0);
106
+ const stateColumns = Math.min(3, Math.max(1, stateCount));
107
+ const stateRows = Math.max(1, Math.ceil(stateCount / stateColumns));
108
+ budget.reserve(stateColumns * 420, 46 + stateRows * 280, 'GUI state gallery');
109
+ const resolutionCount = resolutions?.length ?? 4;
110
+ const resolutionColumns = Math.min(3, Math.max(1, resolutionCount));
111
+ const resolutionRows = Math.max(1, Math.ceil(resolutionCount / resolutionColumns));
112
+ budget.reserve(resolutionColumns * 420, 46 + resolutionRows * 280, 'GUI resolution gallery');
113
+ const scenarioBudget = new RenderBudget();
114
+ const scenarioColumns = Math.min(3, Math.max(1, scenarioCount));
115
+ const scenarioRows = Math.max(1, Math.ceil(scenarioCount / scenarioColumns));
116
+ scenarioBudget.reserve(scenarioColumns * 420, 46 + scenarioRows * 280, 'GUI scripted scenario gallery');
117
+ }
118
+ catch (error) {
119
+ if (error instanceof ServiceError) {
120
+ context.addIssue({ code: 'custom', message: `${error.code}: ${error.message}` });
121
+ return;
122
+ }
123
+ throw error;
124
+ }
125
+ });
126
+ export const guiRewriteRequestSchema = z
127
+ .object({
128
+ mode: z.enum(['source', 'helpers', 'patches']),
129
+ workspaceId: workspaceIdSchema,
130
+ relativePath: workspaceRelativePathSchema,
131
+ windowName: z.string().min(1).max(256),
132
+ scenario: compactGuiScenarioSchema,
133
+ requestKey: backgroundRequestKeySchema.optional(),
134
+ source: z.string().max(SOURCE_MAX_BYTES).optional(),
135
+ helper: compactGuiHelperSchema.optional(),
136
+ additionalFiles: z
137
+ .array(z
138
+ .object({
139
+ relativePath: workspaceRelativePathSchema,
140
+ source: z.string().max(SOURCE_MAX_BYTES),
141
+ })
142
+ .strict())
143
+ .max(GUI_TEXT_PACKAGE_MAX_FILES - 1)
144
+ .optional(),
145
+ expectedSourceHash: z
146
+ .string()
147
+ .regex(/^[a-f0-9]{64}$/u)
148
+ .optional(),
149
+ patches: z
150
+ .array(z
151
+ .object({
152
+ start: z.number().int().min(0),
153
+ end: z.number().int().min(0),
154
+ expectedText: z.string().max(5_000_000),
155
+ text: z.string().max(5_000_000),
156
+ description: z.string().min(1).max(1000),
157
+ })
158
+ .strict())
159
+ .min(1)
160
+ .max(1000)
161
+ .optional(),
162
+ })
163
+ .strict()
164
+ .superRefine((value, context) => {
165
+ if (value.mode === 'source' && value.source === undefined)
166
+ context.addIssue({ code: 'custom', message: 'source is required in source mode' });
167
+ if (value.mode === 'helpers' && value.helper === undefined)
168
+ context.addIssue({ code: 'custom', message: 'helper is required in helpers mode' });
169
+ if (value.mode === 'patches' &&
170
+ (value.patches === undefined || value.expectedSourceHash === undefined))
171
+ context.addIssue({
172
+ code: 'custom',
173
+ message: 'patches and expectedSourceHash are required in patches mode',
174
+ });
175
+ if (value.mode === 'source' && value.helper !== undefined)
176
+ context.addIssue({ code: 'custom', message: 'helper is forbidden in source mode' });
177
+ if (value.mode === 'helpers' && value.source !== undefined)
178
+ context.addIssue({ code: 'custom', message: 'source is forbidden in helpers mode' });
179
+ if (value.mode !== 'patches' &&
180
+ (value.patches !== undefined || value.expectedSourceHash !== undefined))
181
+ context.addIssue({
182
+ code: 'custom',
183
+ message: 'patch fields are accepted only in patches mode',
184
+ });
185
+ if (value.mode === 'patches' &&
186
+ (value.source !== undefined ||
187
+ value.helper !== undefined ||
188
+ value.additionalFiles !== undefined))
189
+ context.addIssue({
190
+ code: 'custom',
191
+ message: 'source, helper, and additionalFiles are forbidden in patches mode',
192
+ });
193
+ });
@@ -0,0 +1,58 @@
1
+ import { z } from 'zod/v4';
2
+ export const helperExpansionRequestSchema = z
3
+ .object({
4
+ rootIds: z.array(z.string().min(1).max(1_024)).min(1).max(2_000).optional(),
5
+ maxDepth: z.number().int().min(1).max(256).optional(),
6
+ maxRecords: z.number().int().min(1).max(5_000).optional(),
7
+ maxWork: z.number().int().min(1).max(100_000).optional(),
8
+ continuationUri: z
9
+ .string()
10
+ .min(1)
11
+ .max(8_192)
12
+ .regex(/^hoi4-agent:\/\//u)
13
+ .optional(),
14
+ })
15
+ .strict();
16
+ const count = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
17
+ export const helperExpansionSummarySchema = z
18
+ .object({
19
+ complete: z.boolean(),
20
+ finished: z.boolean(),
21
+ sourceComplete: z.boolean(),
22
+ maxDepth: count,
23
+ records: count,
24
+ totalRecords: count,
25
+ work: count,
26
+ totalWork: count,
27
+ completedRoots: count,
28
+ totalRoots: count,
29
+ cycles: count,
30
+ depthStops: count,
31
+ continuationUri: z
32
+ .string()
33
+ .max(8_192)
34
+ .regex(/^hoi4-agent:\/\//u)
35
+ .optional(),
36
+ })
37
+ .strict();
38
+ /** A dedicated mode must not silently ignore filters belonging to other inspection modes. */
39
+ export function validateHelperExpansionMode(value, context) {
40
+ const fields = value;
41
+ if (fields.mode !== 'helper_expansion') {
42
+ if (fields.helperExpansion !== undefined)
43
+ context.addIssue({
44
+ code: 'custom',
45
+ path: ['helperExpansion'],
46
+ message: 'helperExpansion requires helper_expansion mode',
47
+ });
48
+ return;
49
+ }
50
+ const allowed = new Set(['workspaceId', 'mode', 'helperExpansion', 'refresh']);
51
+ for (const [field, content] of Object.entries(fields))
52
+ if (content !== undefined && !allowed.has(field))
53
+ context.addIssue({
54
+ code: 'custom',
55
+ path: [field],
56
+ message: 'Use helperExpansion rootIds and bounds in helper_expansion mode',
57
+ });
58
+ }
@@ -0,0 +1,85 @@
1
+ import { z } from 'zod/v4';
2
+ import { MAP_PROVINCE_GEOMETRY_SELECTOR_LIMIT } from '../map/limits.js';
3
+ import { backgroundRequestKeySchema, workspaceIdSchema } from './common.js';
4
+ import { mapAllocationRequestSchema, mapOperationSchema } from './map.js';
5
+ function compact(schema, description) {
6
+ return z.unknown().describe(description).pipe(schema);
7
+ }
8
+ const compactMapOperationSchema = compact(mapOperationSchema, 'Complete map operation.');
9
+ export const mapLayerSchema = z.enum([
10
+ 'province',
11
+ 'state',
12
+ 'strategic-region',
13
+ 'terrain',
14
+ 'continent',
15
+ 'owner',
16
+ 'controller',
17
+ 'cores',
18
+ 'claims',
19
+ 'coast',
20
+ ]);
21
+ export const mapOverlaySchema = z.enum([
22
+ 'coastlines',
23
+ 'ports',
24
+ 'victory-points',
25
+ 'resources',
26
+ 'state-buildings',
27
+ 'province-buildings',
28
+ 'supply-nodes',
29
+ 'railways',
30
+ 'adjacencies',
31
+ 'building-positions',
32
+ 'unit-positions',
33
+ 'weather-positions',
34
+ ]);
35
+ export const mapInspectRequestSchema = z
36
+ .object({
37
+ workspaceId: workspaceIdSchema,
38
+ provinceIds: z
39
+ .array(z.number().int().min(0))
40
+ .max(MAP_PROVINCE_GEOMETRY_SELECTOR_LIMIT)
41
+ .default([])
42
+ .describe(`Export row runs for up to ${MAP_PROVINCE_GEOMETRY_SELECTOR_LIMIT} province IDs`),
43
+ stateIds: z.array(z.number().int().positive()).max(1_000).default([]),
44
+ regionIds: z.array(z.number().int().positive()).max(1_000).default([]),
45
+ query: z.string().max(256).optional(),
46
+ queryLimit: z.number().int().min(1).max(1_000).default(100),
47
+ coordinates: z
48
+ .array(z.discriminatedUnion('kind', [
49
+ z
50
+ .object({
51
+ kind: z.literal('pixel'),
52
+ x: z.number().int().min(0),
53
+ y: z.number().int().min(0),
54
+ })
55
+ .strict(),
56
+ z
57
+ .object({
58
+ kind: z.literal('map'),
59
+ x: z.number(),
60
+ z: z.number(),
61
+ })
62
+ .strict(),
63
+ ]))
64
+ .max(100)
65
+ .default([]),
66
+ includeOverview: z.boolean().default(true),
67
+ allocationRequests: z.array(mapAllocationRequestSchema).max(100).default([]),
68
+ })
69
+ .strict();
70
+ export const mapRenderRequestSchema = z
71
+ .object({
72
+ workspaceId: workspaceIdSchema,
73
+ layer: mapLayerSchema.optional(),
74
+ overlays: z.array(mapOverlaySchema).max(12).optional(),
75
+ scale: z.number().int().min(1).max(16).optional(),
76
+ })
77
+ .strict();
78
+ export const mapRewriteRequestSchema = z
79
+ .object({
80
+ workspaceId: workspaceIdSchema,
81
+ operations: z.array(compactMapOperationSchema).min(1).max(100),
82
+ diffScale: z.number().int().min(1).max(16).optional(),
83
+ requestKey: backgroundRequestKeySchema.optional(),
84
+ })
85
+ .strict();