hoi4-agent-tools 3.0.9 → 3.2.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.
- package/CHANGELOG.md +32 -1
- package/README.md +10 -2
- package/dist/bin/stdio.js +23 -12
- package/dist/hoi4_agent_tools/core/analysis-checkpoints.js +82 -0
- package/dist/hoi4_agent_tools/core/analysis-evidence.js +9 -0
- package/dist/hoi4_agent_tools/core/analysis-job-operations.js +38 -0
- package/dist/hoi4_agent_tools/core/artifact-resource.js +173 -0
- package/dist/hoi4_agent_tools/core/artifacts.js +84 -2
- package/dist/hoi4_agent_tools/core/condition-evaluator.js +16 -0
- package/dist/hoi4_agent_tools/core/dependency-pages.js +216 -0
- package/dist/hoi4_agent_tools/core/dependency-walk.js +266 -0
- package/dist/hoi4_agent_tools/core/domain-validation.js +188 -0
- package/dist/hoi4_agent_tools/core/engine.js +48 -5
- package/dist/hoi4_agent_tools/core/helper-expansion.js +186 -0
- package/dist/hoi4_agent_tools/core/impact-comparison.js +49 -0
- package/dist/hoi4_agent_tools/core/impact-graph.js +301 -0
- package/dist/hoi4_agent_tools/core/impact-semantic.js +256 -0
- package/dist/hoi4_agent_tools/core/index-segments.js +137 -0
- package/dist/hoi4_agent_tools/core/index.js +47 -4
- package/dist/hoi4_agent_tools/core/job-controls.js +109 -0
- package/dist/hoi4_agent_tools/core/job-executor.js +190 -0
- package/dist/hoi4_agent_tools/core/job-service.js +112 -0
- package/dist/hoi4_agent_tools/core/job-store.js +641 -0
- package/dist/hoi4_agent_tools/core/job-transactions.js +126 -0
- package/dist/hoi4_agent_tools/core/job-worker-host.js +202 -0
- package/dist/hoi4_agent_tools/core/job-worker.js +64 -0
- package/dist/hoi4_agent_tools/core/operation-context.js +31 -0
- package/dist/hoi4_agent_tools/core/operation-result.js +412 -0
- package/dist/hoi4_agent_tools/core/operation-tasks.js +387 -0
- package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +383 -0
- package/dist/hoi4_agent_tools/core/result.js +1 -0
- package/dist/hoi4_agent_tools/core/scanner.js +15 -75
- package/dist/hoi4_agent_tools/core/semantic-dependencies.js +233 -0
- package/dist/hoi4_agent_tools/core/shared-request-capacity.js +43 -11
- package/dist/hoi4_agent_tools/core/source/cache.js +107 -0
- package/dist/hoi4_agent_tools/core/source/parser.js +11 -1
- package/dist/hoi4_agent_tools/core/source-overlay.js +125 -0
- package/dist/hoi4_agent_tools/core/transaction-execution.js +134 -0
- package/dist/hoi4_agent_tools/core/transactions.js +80 -27
- package/dist/hoi4_agent_tools/decision/ai.js +104 -0
- package/dist/hoi4_agent_tools/decision/comparison.js +106 -0
- package/dist/hoi4_agent_tools/decision/cost.js +177 -0
- package/dist/hoi4_agent_tools/decision/evaluation.js +256 -0
- package/dist/hoi4_agent_tools/decision/lifecycle.js +114 -0
- package/dist/hoi4_agent_tools/decision/service.js +172 -0
- package/dist/hoi4_agent_tools/decision/source-inventory.js +182 -0
- package/dist/hoi4_agent_tools/event/graph.js +214 -290
- package/dist/hoi4_agent_tools/event/helper-expansion.js +33 -0
- package/dist/hoi4_agent_tools/event/job-operations.js +51 -0
- package/dist/hoi4_agent_tools/event/operations.js +179 -0
- package/dist/hoi4_agent_tools/event/service.js +67 -15
- package/dist/hoi4_agent_tools/event/source-analysis.js +5 -4
- package/dist/hoi4_agent_tools/focus/job-operations.js +77 -0
- package/dist/hoi4_agent_tools/focus/tool-operations.js +669 -0
- package/dist/hoi4_agent_tools/focus/tool-support.js +123 -0
- package/dist/hoi4_agent_tools/gui/job-operations.js +81 -0
- package/dist/hoi4_agent_tools/gui/tool-operations.js +270 -0
- package/dist/hoi4_agent_tools/impact/scenario-suites.js +74 -0
- package/dist/hoi4_agent_tools/impact/service.js +127 -0
- package/dist/hoi4_agent_tools/map/job-operations.js +76 -0
- package/dist/hoi4_agent_tools/map/tool-operations.js +493 -0
- package/dist/hoi4_agent_tools/mcp/prompts/probability.js +25 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register-modern.js +29 -0
- package/dist/hoi4_agent_tools/mcp/resources/register-modern.js +34 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js +4 -150
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +3 -219
- package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +188 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js +18 -6
- package/dist/hoi4_agent_tools/mcp/server/instructions.js +2 -0
- package/dist/hoi4_agent_tools/mcp/server/job-task-store.js +67 -0
- package/dist/hoi4_agent_tools/mcp/server/legacy-task-tools.js +20 -0
- package/dist/hoi4_agent_tools/mcp/server/modern-tasks.js +168 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js +17 -15
- package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +45 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js +2 -412
- package/dist/hoi4_agent_tools/mcp/server/task-request-context.js +9 -0
- package/dist/hoi4_agent_tools/mcp/server/task-tool-catalog.js +16 -0
- package/dist/hoi4_agent_tools/mcp/server/task-tool-definition.js +1 -0
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +1 -123
- package/dist/hoi4_agent_tools/mcp/tools/analysis.js +65 -0
- package/dist/hoi4_agent_tools/mcp/tools/chaosx.js +38 -21
- package/dist/hoi4_agent_tools/mcp/tools/event.js +24 -315
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +34 -1039
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +19 -504
- package/dist/hoi4_agent_tools/mcp/tools/job.js +46 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js +21 -600
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +39 -463
- package/dist/hoi4_agent_tools/mcp/tools/technology.js +40 -304
- package/dist/hoi4_agent_tools/mcp/transports/http.js +58 -1
- package/dist/hoi4_agent_tools/mcp/transports/modern-task-routing.js +109 -0
- package/dist/hoi4_agent_tools/mcp/transports/negotiated-stdio.js +121 -0
- package/dist/hoi4_agent_tools/probability/job-operations.js +58 -0
- package/dist/hoi4_agent_tools/probability/operations.js +183 -0
- package/dist/hoi4_agent_tools/probability/service.js +24 -3
- package/dist/hoi4_agent_tools/schemas/analysis.js +228 -0
- package/dist/hoi4_agent_tools/schemas/common.js +2 -0
- package/dist/hoi4_agent_tools/schemas/event.js +72 -1
- package/dist/hoi4_agent_tools/schemas/focus-requests.js +189 -0
- package/dist/hoi4_agent_tools/schemas/gui-requests.js +193 -0
- package/dist/hoi4_agent_tools/schemas/helper-expansion.js +58 -0
- package/dist/hoi4_agent_tools/schemas/map-requests.js +85 -0
- package/dist/hoi4_agent_tools/schemas/probability-requests.js +168 -0
- package/dist/hoi4_agent_tools/schemas/technology.js +90 -1
- package/dist/hoi4_agent_tools/schemas/transaction.js +1 -0
- package/dist/hoi4_agent_tools/technology/graph.js +83 -78
- package/dist/hoi4_agent_tools/technology/helper-expansion.js +33 -0
- package/dist/hoi4_agent_tools/technology/job-operations.js +60 -0
- package/dist/hoi4_agent_tools/technology/operations.js +125 -0
- package/dist/hoi4_agent_tools/technology/service.js +89 -15
- package/dist/hoi4_agent_tools/technology/source-analysis.js +2 -1
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +3 -0
- package/docs/analysis.md +100 -0
- package/docs/development.md +2 -0
- package/docs/events.md +7 -2
- package/docs/helper-expansion.md +78 -0
- package/docs/http.md +2 -0
- package/docs/jobs.md +86 -0
- package/docs/technology.md +6 -2
- package/package.json +15 -6
- package/schemas/configuration.schema.json +1 -1
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/decision-inspect-input.schema.json +290 -0
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/helper-expansion-request.schema.json +44 -0
- package/schemas/helper-expansion-summary.schema.json +70 -0
- package/schemas/impact-inspect-input.schema.json +203 -0
- package/schemas/map-operation.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +1 -1
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/server.json +3 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
2
|
import { SOURCE_MAX_BYTES } from '../core/source/index.js';
|
|
3
|
-
import { workspaceRelativePathSchema } from './common.js';
|
|
3
|
+
import { workspaceIdSchema, workspaceRelativePathSchema } from './common.js';
|
|
4
|
+
import { helperExpansionRequestSchema, validateHelperExpansionMode } from './helper-expansion.js';
|
|
4
5
|
const eventIdSchema = z.string().min(1).max(256);
|
|
5
6
|
const eventSourcePathSchema = z.string().min(1).max(1024);
|
|
6
7
|
const eventNodeIdSchema = z.string().min(1).max(1024);
|
|
@@ -13,6 +14,7 @@ export const eventInspectModeSchema = z.enum([
|
|
|
13
14
|
'state_flow',
|
|
14
15
|
'lint',
|
|
15
16
|
'impact',
|
|
17
|
+
'helper_expansion',
|
|
16
18
|
]);
|
|
17
19
|
export const eventDirectionSchema = z.enum(['upstream', 'downstream', 'both']);
|
|
18
20
|
export const eventRenderViewSchema = z.enum([
|
|
@@ -93,3 +95,72 @@ export const eventProposedSourceSchema = z
|
|
|
93
95
|
expectedSourceHash: eventSha256Schema.optional(),
|
|
94
96
|
})
|
|
95
97
|
.strict();
|
|
98
|
+
export function validateEventInspectRequest(value, context) {
|
|
99
|
+
validateHelperExpansionMode(value, context);
|
|
100
|
+
if (value.mode === 'trace' && value.selector === undefined)
|
|
101
|
+
context.addIssue({ code: 'custom', path: ['selector'], message: 'Trace requires selector' });
|
|
102
|
+
if (value.mode === 'explain_path' && (value.from === undefined || value.to === undefined))
|
|
103
|
+
context.addIssue({
|
|
104
|
+
code: 'custom',
|
|
105
|
+
path: value.from === undefined ? ['from'] : ['to'],
|
|
106
|
+
message: 'Path explanation requires from and to',
|
|
107
|
+
});
|
|
108
|
+
if (value.mode === 'impact' && value.impactSubject === undefined)
|
|
109
|
+
context.addIssue({
|
|
110
|
+
code: 'custom',
|
|
111
|
+
path: ['impactSubject'],
|
|
112
|
+
message: 'Impact analysis requires impactSubject',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
export const eventInspectRequestSchema = z
|
|
116
|
+
.object({
|
|
117
|
+
workspaceId: workspaceIdSchema,
|
|
118
|
+
mode: eventInspectModeSchema,
|
|
119
|
+
selector: eventSelectorSchema.optional(),
|
|
120
|
+
from: eventSelectorSchema.optional(),
|
|
121
|
+
to: eventSelectorSchema.optional(),
|
|
122
|
+
direction: eventDirectionSchema.optional(),
|
|
123
|
+
maxDepth: z.number().int().min(1).max(64).optional(),
|
|
124
|
+
maxNodes: z.number().int().min(1).max(5_000).optional(),
|
|
125
|
+
maxEdges: z.number().int().min(1).max(20_000).optional(),
|
|
126
|
+
expandHelpers: z.boolean().optional(),
|
|
127
|
+
stateSubject: eventStateSubjectSchema.optional(),
|
|
128
|
+
impactSubject: eventImpactSubjectSchema.optional(),
|
|
129
|
+
helperExpansion: helperExpansionRequestSchema.optional(),
|
|
130
|
+
refresh: z.boolean().optional(),
|
|
131
|
+
})
|
|
132
|
+
.strict()
|
|
133
|
+
.superRefine(validateEventInspectRequest);
|
|
134
|
+
export const eventRenderRequestSchema = z
|
|
135
|
+
.object({
|
|
136
|
+
workspaceId: workspaceIdSchema,
|
|
137
|
+
view: eventRenderViewSchema,
|
|
138
|
+
selector: eventSelectorSchema.optional(),
|
|
139
|
+
direction: eventDirectionSchema.optional(),
|
|
140
|
+
maxDepth: z.number().int().min(1).max(64).optional(),
|
|
141
|
+
maxNodes: z.number().int().min(1).max(240).optional(),
|
|
142
|
+
expandHelpers: z.boolean().optional(),
|
|
143
|
+
includeHtml: z.boolean().optional(),
|
|
144
|
+
refresh: z.boolean().optional(),
|
|
145
|
+
})
|
|
146
|
+
.strict();
|
|
147
|
+
export function validateEventCompareRequest(value, context) {
|
|
148
|
+
if (value.after !== undefined && value.proposedSources !== undefined)
|
|
149
|
+
context.addIssue({
|
|
150
|
+
code: 'custom',
|
|
151
|
+
path: ['after'],
|
|
152
|
+
message: 'after and proposedSources are mutually exclusive',
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
export const eventCompareRequestSchema = z
|
|
156
|
+
.object({
|
|
157
|
+
workspaceId: workspaceIdSchema,
|
|
158
|
+
before: eventGraphReferenceSchema.optional(),
|
|
159
|
+
after: eventGraphReferenceSchema.optional(),
|
|
160
|
+
proposedSources: z.array(eventProposedSourceSchema).min(1).max(64).optional(),
|
|
161
|
+
render: z.boolean().optional(),
|
|
162
|
+
maxRenderNodes: z.number().int().min(1).max(240).optional(),
|
|
163
|
+
refresh: z.boolean().optional(),
|
|
164
|
+
})
|
|
165
|
+
.strict()
|
|
166
|
+
.superRefine(validateEventCompareRequest);
|
|
@@ -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();
|