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.
- package/CHANGELOG.md +35 -1
- package/README.md +4 -0
- package/dist/bin/stdio.js +23 -12
- package/dist/hoi4_agent_tools/core/analysis-checkpoints.js +82 -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 +399 -0
- package/dist/hoi4_agent_tools/core/condition-model.js +1 -0
- package/dist/hoi4_agent_tools/core/condition-schema.js +42 -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/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 +200 -0
- package/dist/hoi4_agent_tools/core/job-worker.js +62 -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 +382 -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/scenario-state.js +97 -0
- package/dist/hoi4_agent_tools/core/semantic-dependencies.js +233 -0
- package/dist/hoi4_agent_tools/core/shared-request-capacity.js +74 -25
- 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/transaction-execution.js +134 -0
- package/dist/hoi4_agent_tools/core/transactions.js +80 -27
- 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/scenario-conditions.js +119 -0
- package/dist/hoi4_agent_tools/gui/scenario-generator.js +59 -73
- package/dist/hoi4_agent_tools/gui/scenario.js +3 -0
- package/dist/hoi4_agent_tools/gui/studio.js +5 -2
- package/dist/hoi4_agent_tools/gui/tool-operations.js +270 -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 +16 -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 +43 -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 +14 -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/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/scenario-state.js +2 -95
- package/dist/hoi4_agent_tools/probability/service.js +24 -3
- package/dist/hoi4_agent_tools/probability/trigger-evaluator.js +1 -287
- 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 +2 -0
- package/docs/development.md +2 -0
- package/docs/events.md +7 -2
- package/docs/gui.md +3 -1
- package/docs/helper-expansion.md +78 -0
- package/docs/http.md +2 -0
- package/docs/jobs.md +85 -0
- package/docs/technology.md +6 -2
- package/package.json +11 -5
- 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/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 +189 -33
- package/schemas/helper-expansion-request.schema.json +44 -0
- package/schemas/helper-expansion-summary.schema.json +70 -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 +2 -2
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { compareCodeUnits } from '../core/canonical.js';
|
|
2
|
+
import { ServiceError } from '../core/result.js';
|
|
3
|
+
import { parseClausewitz } from '../core/source/index.js';
|
|
4
|
+
import { enrichFocusPlanFromSidecar, focusPlanningSidecarPath, importContinuousFocusPalettes, importFocusTrees, linkContinuousFocusPalettes, parseFocusPlanningSidecar, } from './index.js';
|
|
5
|
+
function normalizedRoot(value) {
|
|
6
|
+
return value.replaceAll('\\', '/').replace(/^\.\//u, '').replace(/\/+$/u, '').toLowerCase();
|
|
7
|
+
}
|
|
8
|
+
export function underConfiguredRoot(relativePath, roots) {
|
|
9
|
+
const candidate = relativePath.replaceAll('\\', '/').toLowerCase();
|
|
10
|
+
return roots.some((root) => {
|
|
11
|
+
const normalized = normalizedRoot(root);
|
|
12
|
+
return candidate === normalized || candidate.startsWith(`${normalized}/`);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function activeFocusFiles(snapshot, focusRoots) {
|
|
16
|
+
return snapshot.files.filter(({ relativePath, shadowedBy }) => shadowedBy === undefined &&
|
|
17
|
+
underConfiguredRoot(relativePath, focusRoots) &&
|
|
18
|
+
relativePath.toLowerCase().endsWith('.txt'));
|
|
19
|
+
}
|
|
20
|
+
export function activeContinuousFocusFiles(snapshot) {
|
|
21
|
+
return snapshot.files.filter(({ relativePath, shadowedBy }) => shadowedBy === undefined &&
|
|
22
|
+
relativePath.replaceAll('\\', '/').toLowerCase().startsWith('common/continuous_focus/') &&
|
|
23
|
+
relativePath.toLowerCase().endsWith('.txt'));
|
|
24
|
+
}
|
|
25
|
+
export function continuousPalettes(snapshot) {
|
|
26
|
+
const palettes = [];
|
|
27
|
+
const diagnostics = [];
|
|
28
|
+
for (const file of activeContinuousFocusFiles(snapshot)) {
|
|
29
|
+
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
30
|
+
const imported = importContinuousFocusPalettes(document);
|
|
31
|
+
palettes.push(...imported.continuousFocusPalettes);
|
|
32
|
+
diagnostics.push(...imported.diagnostics);
|
|
33
|
+
}
|
|
34
|
+
return { palettes, diagnostics };
|
|
35
|
+
}
|
|
36
|
+
export function activeSidecar(snapshot, focusFile) {
|
|
37
|
+
const relativePath = focusPlanningSidecarPath(focusFile.relativePath).toLowerCase();
|
|
38
|
+
const file = snapshot.files.find((candidate) => candidate.shadowedBy === undefined && candidate.relativePath.toLowerCase() === relativePath);
|
|
39
|
+
return file === undefined ? undefined : { file, sidecar: parseFocusPlanningSidecar(file.bytes) };
|
|
40
|
+
}
|
|
41
|
+
export function importFocusFile(snapshot, file, palettes) {
|
|
42
|
+
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
43
|
+
const imported = importFocusTrees(document, { references: referenceCatalog(snapshot) });
|
|
44
|
+
const sidecar = activeSidecar(snapshot, file);
|
|
45
|
+
imported.plans = imported.plans.map((plan) => {
|
|
46
|
+
const enriched = sidecar === undefined
|
|
47
|
+
? { plan, diagnostics: [] }
|
|
48
|
+
: enrichFocusPlanFromSidecar(plan, sidecar.sidecar);
|
|
49
|
+
imported.diagnostics.push(...enriched.diagnostics);
|
|
50
|
+
return linkContinuousFocusPalettes(enriched.plan, palettes);
|
|
51
|
+
});
|
|
52
|
+
return imported;
|
|
53
|
+
}
|
|
54
|
+
export function selectedFocusFile(snapshot, focusRoots, relativePath) {
|
|
55
|
+
const file = activeFocusFiles(snapshot, focusRoots).find((candidate) => candidate.relativePath === relativePath);
|
|
56
|
+
if (file === undefined)
|
|
57
|
+
throw new ServiceError('FOCUS_SOURCE_NOT_FOUND', `Focus source was not found: ${relativePath}`);
|
|
58
|
+
return file;
|
|
59
|
+
}
|
|
60
|
+
export function selectedContinuousFocusFile(snapshot, relativePath) {
|
|
61
|
+
const file = activeContinuousFocusFiles(snapshot).find((candidate) => candidate.relativePath === relativePath);
|
|
62
|
+
if (file === undefined)
|
|
63
|
+
throw new ServiceError('CONTINUOUS_FOCUS_SOURCE_NOT_FOUND', `Continuous focus source was not found: ${relativePath}`);
|
|
64
|
+
return file;
|
|
65
|
+
}
|
|
66
|
+
export function selectPlan(result, treeId) {
|
|
67
|
+
if (result.plans.length === 0)
|
|
68
|
+
throw new ServiceError('FOCUS_TREE_NOT_FOUND', 'The source file contains no focus tree');
|
|
69
|
+
if (treeId === undefined) {
|
|
70
|
+
if (result.plans.length !== 1)
|
|
71
|
+
throw new ServiceError('FOCUS_TREE_ID_REQUIRED', 'The source file contains multiple focus trees; provide treeId', { treeIds: result.plans.map(({ id }) => id) });
|
|
72
|
+
return result.plans[0];
|
|
73
|
+
}
|
|
74
|
+
const plan = result.plans.find(({ id }) => id === treeId);
|
|
75
|
+
if (plan === undefined)
|
|
76
|
+
throw new ServiceError('FOCUS_TREE_NOT_FOUND', `Focus tree was not found: ${treeId}`);
|
|
77
|
+
return plan;
|
|
78
|
+
}
|
|
79
|
+
export function selectContinuousPlan(result, paletteId) {
|
|
80
|
+
if (result.continuousFocusPalettes.length === 0)
|
|
81
|
+
throw new ServiceError('CONTINUOUS_FOCUS_PALETTE_NOT_FOUND', 'The source file contains no continuous focus palette');
|
|
82
|
+
if (paletteId === undefined) {
|
|
83
|
+
if (result.continuousFocusPalettes.length !== 1)
|
|
84
|
+
throw new ServiceError('CONTINUOUS_FOCUS_PALETTE_ID_REQUIRED', 'The source file contains multiple continuous focus palettes; provide paletteId', { paletteIds: result.continuousFocusPalettes.map(({ id }) => id) });
|
|
85
|
+
return result.continuousFocusPalettes[0];
|
|
86
|
+
}
|
|
87
|
+
const plan = result.continuousFocusPalettes.find(({ id }) => id === paletteId);
|
|
88
|
+
if (plan === undefined)
|
|
89
|
+
throw new ServiceError('CONTINUOUS_FOCUS_PALETTE_NOT_FOUND', `Continuous focus palette was not found: ${paletteId}`);
|
|
90
|
+
return plan;
|
|
91
|
+
}
|
|
92
|
+
export function referenceCatalog(snapshot) {
|
|
93
|
+
const identifiers = (kind) => [
|
|
94
|
+
...new Set(snapshot.index
|
|
95
|
+
.findAll(kind)
|
|
96
|
+
.filter(({ overridden }) => !overridden)
|
|
97
|
+
.map(({ id }) => id)),
|
|
98
|
+
].sort((left, right) => compareCodeUnits(left, right));
|
|
99
|
+
return {
|
|
100
|
+
decision: identifiers('decision'),
|
|
101
|
+
decision_category: identifiers('decision_category'),
|
|
102
|
+
event: identifiers('event'),
|
|
103
|
+
idea: identifiers('idea'),
|
|
104
|
+
leader: identifiers('leader'),
|
|
105
|
+
formable: identifiers('formable'),
|
|
106
|
+
helper: identifiers('scripted_effect'),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export function validationFromDiagnostics(diagnostics) {
|
|
110
|
+
const blocking = diagnostics.filter(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
111
|
+
return {
|
|
112
|
+
passed: blocking.length === 0,
|
|
113
|
+
checks: [
|
|
114
|
+
{
|
|
115
|
+
id: 'focus-diagnostics',
|
|
116
|
+
passed: blocking.length === 0,
|
|
117
|
+
message: blocking.length === 0
|
|
118
|
+
? 'Focus source, references, and layout have no blocking diagnostics'
|
|
119
|
+
: `${blocking.length} blocking focus diagnostics`,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
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 { guiInspectRequestSchema, guiRenderRequestSchema, guiRewriteRequestSchema, } from '../schemas/gui-requests.js';
|
|
6
|
+
import { ScriptedGuiStudio } from './studio.js';
|
|
7
|
+
import { inspectGui, completeGuiRewrite, normalizeGuiInspectRequest, normalizeGuiRenderRequest, normalizeGuiRewriteRequest, prepareGuiRewrite, renderGui, } from './tool-operations.js';
|
|
8
|
+
const wireResult = z.record(z.string(), z.json());
|
|
9
|
+
/** Registers read-only GUI operations without invoking transport handlers. */
|
|
10
|
+
export function registerGuiJobs(operations, engine) {
|
|
11
|
+
const studio = new ScriptedGuiStudio(engine);
|
|
12
|
+
const lifetime = new IdleCacheLifetime(() => studio.clearCaches());
|
|
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(studio, 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
|
+
'sourceRevision' in result.data &&
|
|
29
|
+
typeof result.data.sourceRevision === 'string'
|
|
30
|
+
? result.data.sourceRevision
|
|
31
|
+
: result.data !== null &&
|
|
32
|
+
typeof result.data === 'object' &&
|
|
33
|
+
'sharedRevision' in result.data &&
|
|
34
|
+
typeof result.data.sharedRevision === 'string'
|
|
35
|
+
? result.data.sharedRevision
|
|
36
|
+
: undefined;
|
|
37
|
+
if (revision !== undefined)
|
|
38
|
+
await context.stageResult(wire, { sourceRevision: revision });
|
|
39
|
+
return wire;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (context.signal.aborted)
|
|
43
|
+
throw error;
|
|
44
|
+
return wireResult.parse(JSON.parse(JSON.stringify(errorResult(error, context.workspaceId))));
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
release();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
register('hoi4.gui_inspect', guiInspectRequestSchema, normalizeGuiInspectRequest, (studio, input, context) => inspectGui(engine, studio, input, context));
|
|
52
|
+
register('hoi4.gui_render', guiRenderRequestSchema, normalizeGuiRenderRequest, renderGui);
|
|
53
|
+
operations.registerWrite('hoi4.gui_rewrite', guiRewriteRequestSchema, async (input, context) => {
|
|
54
|
+
if (input.workspaceId !== 'current' && input.workspaceId !== context.workspaceId)
|
|
55
|
+
throw new ServiceError('JOB_ARGUMENT_SCOPE_MISMATCH', 'The request workspace does not match the authorized job workspace');
|
|
56
|
+
const release = lifetime.begin();
|
|
57
|
+
try {
|
|
58
|
+
return await prepareGuiRewrite(studio, normalizeGuiRewriteRequest(input), {
|
|
59
|
+
workspaceId: context.workspaceId,
|
|
60
|
+
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
61
|
+
signal: context.signal,
|
|
62
|
+
progress: (completed, total, message) => context.progress({ completed, total, message }),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
release();
|
|
67
|
+
}
|
|
68
|
+
}, async (_input, execution, _reconciled, recipe, context) => {
|
|
69
|
+
const result = completeGuiRewrite(context.workspaceId, execution, recipe);
|
|
70
|
+
await context.progress({
|
|
71
|
+
completed: 3,
|
|
72
|
+
total: 3,
|
|
73
|
+
message: execution.outcome === 'applied'
|
|
74
|
+
? 'GUI rewrite complete'
|
|
75
|
+
: execution.outcome === 'unchanged'
|
|
76
|
+
? 'GUI content already satisfied the rewrite'
|
|
77
|
+
: 'GUI rewrite blocked',
|
|
78
|
+
});
|
|
79
|
+
return wireResult.parse(JSON.parse(JSON.stringify(toolResult(result))));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ClausewitzEvaluationDefinitions } from '../core/clausewitz-evaluation.js';
|
|
2
|
+
import { hasBlockingDiagnostics } from '../core/diagnostics.js';
|
|
3
|
+
import { evaluateTriggerBlock } from '../core/condition-evaluator.js';
|
|
4
|
+
import { resolveScopeContext, rootScopeContext } from '../core/scenario-state.js';
|
|
5
|
+
import { assignments, firstScalar, parseClausewitz } from '../core/source/index.js';
|
|
6
|
+
/** Keep declared scoped scalars out of exploratory value generation. */
|
|
7
|
+
export function guiExplicitConditionValues(base) {
|
|
8
|
+
const values = { ...base.stateValues, ...base.variables, ...base.values };
|
|
9
|
+
for (const [key, value] of Object.entries(values)) {
|
|
10
|
+
if (key.includes('.'))
|
|
11
|
+
continue;
|
|
12
|
+
values[`ROOT.${key}`] ??= value;
|
|
13
|
+
values[`THIS.${key}`] ??= value;
|
|
14
|
+
}
|
|
15
|
+
for (const [scope, binding] of Object.entries(base.scopes ?? {})) {
|
|
16
|
+
for (const [key, value] of Object.entries(binding.state)) {
|
|
17
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
|
|
18
|
+
values[`${scope}.${key}`] ??= value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return values;
|
|
22
|
+
}
|
|
23
|
+
function parsedCondition(expression) {
|
|
24
|
+
const text = expression.trim();
|
|
25
|
+
const document = parseClausewitz(Buffer.from(`condition = ${text.startsWith('{') ? text : `{ ${text} }`}`), 'scenario:condition');
|
|
26
|
+
const assignment = assignments(document.root, 'condition')[0];
|
|
27
|
+
return {
|
|
28
|
+
...(assignment?.value.type === 'block' ? { block: assignment.value } : {}),
|
|
29
|
+
invalid: hasBlockingDiagnostics(document.diagnostics) || assignment?.value.type !== 'block',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function guiConditionScenario(base, values = base.values) {
|
|
33
|
+
const state = { ...base.stateValues, ...base.variables, ...values };
|
|
34
|
+
for (const [key, value] of Object.entries(base.flags))
|
|
35
|
+
state[`flag.${key}`] = value;
|
|
36
|
+
const scopes = structuredClone(base.scopes ?? {});
|
|
37
|
+
for (const [key, value] of Object.entries(state)) {
|
|
38
|
+
const match = /^(ROOT|THIS|FROM(?:\.FROM)*|PREV)\.(.+)$/u.exec(key);
|
|
39
|
+
if (match === null)
|
|
40
|
+
continue;
|
|
41
|
+
if (match[1] === 'ROOT' || match[1] === 'THIS')
|
|
42
|
+
state[match[2]] ??= value;
|
|
43
|
+
else {
|
|
44
|
+
const scope = (scopes[match[1]] ??= { id: match[1], state: {} });
|
|
45
|
+
scope.state[match[2]] ??= value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const actor = base.country?.tag ?? base.country?.countryTag ?? base.country?.GetTag;
|
|
49
|
+
return {
|
|
50
|
+
id: base.id,
|
|
51
|
+
state,
|
|
52
|
+
scopes,
|
|
53
|
+
closedFlags: false,
|
|
54
|
+
...(typeof actor === 'string' ? { actor } : {}),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function evaluateGuiCondition(expression, scenario, subject, definitions = new ClausewitzEvaluationDefinitions(), scope = 'ROOT') {
|
|
58
|
+
const context = resolveScopeContext(scenario, scope, rootScopeContext(scenario));
|
|
59
|
+
const parsed = expression === undefined ? { invalid: false } : parsedCondition(expression);
|
|
60
|
+
if (parsed.invalid || context === undefined)
|
|
61
|
+
return {
|
|
62
|
+
state: 'unresolved',
|
|
63
|
+
unresolved: [
|
|
64
|
+
{
|
|
65
|
+
code: 'GUI_CONDITION_UNRESOLVED',
|
|
66
|
+
message: parsed.invalid
|
|
67
|
+
? 'Condition could not be parsed'
|
|
68
|
+
: `Scenario does not declare scope ${scope}`,
|
|
69
|
+
candidateId: subject.id,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
return evaluateTriggerBlock(parsed.block, scenario, subject, definitions, [], context);
|
|
74
|
+
}
|
|
75
|
+
/** Find operands structurally for exploratory previews, without asserting branch eligibility. */
|
|
76
|
+
export function guiConditionVariables(expression, definitions) {
|
|
77
|
+
if (expression === undefined)
|
|
78
|
+
return [];
|
|
79
|
+
const parsed = parsedCondition(expression);
|
|
80
|
+
if (parsed.invalid || parsed.block === undefined)
|
|
81
|
+
return [];
|
|
82
|
+
const output = new Set();
|
|
83
|
+
const visit = (block, scope, stack) => {
|
|
84
|
+
const add = (key) => {
|
|
85
|
+
// Scoped operands need actual bindings; fabricated flat values can shadow ROOT/PREV.
|
|
86
|
+
if (scope === '' &&
|
|
87
|
+
key !== undefined &&
|
|
88
|
+
!key.includes('.') &&
|
|
89
|
+
!key.startsWith('@') &&
|
|
90
|
+
!key.startsWith('constant:') &&
|
|
91
|
+
!Number.isFinite(Number(key)))
|
|
92
|
+
output.add(key);
|
|
93
|
+
};
|
|
94
|
+
for (const assignment of assignments(block)) {
|
|
95
|
+
const key = assignment.key.value;
|
|
96
|
+
if (key === 'check_variable' && assignment.value.type === 'block') {
|
|
97
|
+
const short = assignments(assignment.value).find(({ key }) => !['var', 'value', 'compare', 'tooltip'].includes(key.value));
|
|
98
|
+
add(firstScalar(assignment.value, 'var')?.value ?? short?.key.value);
|
|
99
|
+
add(firstScalar(assignment.value, 'value')?.value ??
|
|
100
|
+
(short?.value.type === 'scalar' ? short.value.value : undefined));
|
|
101
|
+
}
|
|
102
|
+
else if (assignment.value.type === 'block') {
|
|
103
|
+
const prefix = ['ROOT', 'THIS', 'FROM', 'PREV'].includes(key) ? `${key}.` : scope;
|
|
104
|
+
visit(assignment.value, prefix, stack);
|
|
105
|
+
}
|
|
106
|
+
else if (assignment.operator.text === '>' || assignment.operator.text === '<') {
|
|
107
|
+
add(key);
|
|
108
|
+
add(assignment.value.value);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const helper = definitions.scriptedTriggers.get(key);
|
|
112
|
+
if (helper !== undefined && !stack.has(key))
|
|
113
|
+
visit(helper.value, scope, new Set([...stack, key]));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
visit(parsed.block, '', new Set());
|
|
118
|
+
return [...output].sort();
|
|
119
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { sha256Bytes } from '../core/canonical.js';
|
|
2
|
+
import { ClausewitzEvaluationDefinitions } from '../core/clausewitz-evaluation.js';
|
|
3
|
+
import { evaluateGuiCondition, guiConditionScenario, guiConditionVariables, guiExplicitConditionValues, } from './scenario-conditions.js';
|
|
2
4
|
import { parsePreviewScenario } from './scenario.js';
|
|
3
5
|
const countryNames = ['Germany', 'France', 'Poland', 'Italy', 'Brazil', 'India', 'Canada'];
|
|
4
6
|
const countryTags = ['GER', 'FRA', 'POL', 'ITA', 'BRA', 'RAJ', 'CAN'];
|
|
@@ -257,70 +259,46 @@ function generatedNumberForKey(key, options, random, inferred = {}) {
|
|
|
257
259
|
function setIfMissing(target, key, value) {
|
|
258
260
|
target[key] ??= value;
|
|
259
261
|
}
|
|
260
|
-
function scriptedLocalisationValue(graph, token, language,
|
|
262
|
+
function scriptedLocalisationValue(graph, token, language, kind, values, base, definitions, results) {
|
|
261
263
|
const shortToken = token.slice(token.lastIndexOf('.') + 1);
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
+
const definition = graph.scriptedLocalisation
|
|
265
|
+
.filter(({ name }) => name === token || name === shortToken)
|
|
266
|
+
.at(-1);
|
|
267
|
+
if (definition === undefined)
|
|
264
268
|
return undefined;
|
|
265
|
-
const
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const firstThreshold = evaluated
|
|
278
|
-
.map(({ choice }) => simpleThreshold(choice.triggerExpression))
|
|
279
|
-
.find((threshold) => threshold !== undefined && typeof values[threshold.variable] === 'number');
|
|
280
|
-
const unknownExists = evaluated.some(({ result }) => result === undefined);
|
|
281
|
-
const ranked = matched === undefined && unknownExists && firstThreshold !== undefined
|
|
282
|
-
? eligible[Math.min(eligible.length - 1, Math.max(0, Math.round((1 - Math.max(0, Math.min(100, Number(values[firstThreshold.variable]))) / 100) *
|
|
283
|
-
(eligible.length - 1))))]
|
|
284
|
-
: undefined;
|
|
285
|
-
const fallback = evaluated.find(({ result }) => result === 'fallback')?.choice;
|
|
286
|
-
const selected = matched ?? ranked ?? fallback ?? random.pick(eligible);
|
|
287
|
-
if (kind === 'image')
|
|
288
|
-
return selected.localisationKey;
|
|
289
|
-
return localisationText(graph, language, selected.localisationKey);
|
|
290
|
-
}
|
|
291
|
-
function simpleThreshold(triggerExpression) {
|
|
292
|
-
if (triggerExpression === undefined)
|
|
293
|
-
return undefined;
|
|
294
|
-
const direct = /([A-Za-z_][A-Za-z0-9_.:^]*)\s*([<>])\s*(-?(?:\d+\.?\d*|\.\d+))/u.exec(triggerExpression);
|
|
295
|
-
if (direct !== null)
|
|
296
|
-
return {
|
|
297
|
-
variable: direct[1],
|
|
298
|
-
operator: direct[2],
|
|
299
|
-
threshold: Number(direct[3]),
|
|
300
|
-
};
|
|
301
|
-
const variable = /\bvar\s*=\s*([A-Za-z_][A-Za-z0-9_.:^]*)/u.exec(triggerExpression)?.[1];
|
|
302
|
-
const threshold = /\bvalue\s*=\s*(-?(?:\d+\.?\d*|\.\d+))/u.exec(triggerExpression)?.[1];
|
|
303
|
-
const compare = /\bcompare\s*=\s*(greater_than|less_than)/u.exec(triggerExpression)?.[1];
|
|
304
|
-
if (variable === undefined || threshold === undefined || compare === undefined)
|
|
305
|
-
return undefined;
|
|
306
|
-
return {
|
|
307
|
-
variable,
|
|
308
|
-
operator: compare === 'greater_than' ? '>' : '<',
|
|
309
|
-
threshold: Number(threshold),
|
|
269
|
+
const scenario = guiConditionScenario(base, values);
|
|
270
|
+
const subject = {
|
|
271
|
+
id: token,
|
|
272
|
+
provenance: [
|
|
273
|
+
{
|
|
274
|
+
path: definition.sourcePath,
|
|
275
|
+
rootKind: 'source',
|
|
276
|
+
loadOrder: 0,
|
|
277
|
+
sourceHash: graph.sourceHashes[definition.sourcePath] ?? '',
|
|
278
|
+
...(definition.location === undefined ? {} : { location: definition.location }),
|
|
279
|
+
},
|
|
280
|
+
],
|
|
310
281
|
};
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
282
|
+
const prefix = token.includes('.') ? token.slice(0, token.lastIndexOf('.')) : 'ROOT';
|
|
283
|
+
for (const choice of definition.choices) {
|
|
284
|
+
const result = evaluateGuiCondition(choice.triggerExpression, scenario, subject, definitions, prefix);
|
|
285
|
+
results.push({
|
|
286
|
+
token,
|
|
287
|
+
sourcePath: definition.sourcePath,
|
|
288
|
+
state: result.state,
|
|
289
|
+
localisationKey: choice.localisationKey,
|
|
290
|
+
unresolved: result.unresolved.map(({ message }) => message),
|
|
291
|
+
});
|
|
292
|
+
// An earlier unknown branch may be selected by the game; a later fallback cannot prove it false.
|
|
293
|
+
if (result.state === 'unresolved')
|
|
294
|
+
return undefined;
|
|
295
|
+
if (result.state === 'false')
|
|
296
|
+
continue;
|
|
297
|
+
return kind === 'image'
|
|
298
|
+
? choice.localisationKey
|
|
299
|
+
: localisationText(graph, language, choice.localisationKey);
|
|
300
|
+
}
|
|
301
|
+
return undefined;
|
|
324
302
|
}
|
|
325
303
|
function materializeDynamicText(template, values) {
|
|
326
304
|
const valueFor = (key) => {
|
|
@@ -481,7 +459,7 @@ function generatedRows(count, random, options) {
|
|
|
481
459
|
};
|
|
482
460
|
});
|
|
483
461
|
}
|
|
484
|
-
export function generateGuiPreviewScenarios(graph, windowName, base, options) {
|
|
462
|
+
export function generateGuiPreviewScenarios(graph, windowName, base, options, definitions = new ClausewitzEvaluationDefinitions()) {
|
|
485
463
|
if (!options.enabled)
|
|
486
464
|
return [];
|
|
487
465
|
const scripted = relatedScriptedGuis(graph, windowName);
|
|
@@ -519,9 +497,8 @@ export function generateGuiPreviewScenarios(graph, windowName, base, options) {
|
|
|
519
497
|
if (![...relevantDynamicTokens].some((token) => token === definition.name || token.endsWith(`.${definition.name}`)))
|
|
520
498
|
continue;
|
|
521
499
|
for (const choice of definition.choices) {
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
tokens.numeric.add(threshold.variable);
|
|
500
|
+
for (const variable of guiConditionVariables(choice.triggerExpression, definitions))
|
|
501
|
+
tokens.numeric.add(variable);
|
|
525
502
|
}
|
|
526
503
|
}
|
|
527
504
|
const progressRanges = new Map();
|
|
@@ -545,7 +522,10 @@ export function generateGuiPreviewScenarios(graph, windowName, base, options) {
|
|
|
545
522
|
const sourceSeed = options.seed === 'auto' ? `${windowName}:${base.id}:${hashSourceGraph(graph)}` : options.seed;
|
|
546
523
|
return Array.from({ length: options.count }, (_unused, scenarioIndex) => {
|
|
547
524
|
const random = new ScenarioRandom(`${sourceSeed}:${scenarioIndex}`);
|
|
548
|
-
const
|
|
525
|
+
const explicitValues = guiExplicitConditionValues(base);
|
|
526
|
+
const values = { ...explicitValues };
|
|
527
|
+
const conditionResults = [];
|
|
528
|
+
const scriptedTokens = new Set([...tokens.textual].filter((token) => graph.scriptedLocalisation.some(({ name }) => name === token || token.endsWith(`.${name}`))));
|
|
549
529
|
const lists = Object.fromEntries(Object.entries(base.lists).map(([key, rows]) => [key, rows.map((row) => ({ ...row }))]));
|
|
550
530
|
const visibility = generatedVisibility(graph, scripted, options, random, base.visibility);
|
|
551
531
|
const elementStates = { ...base.elementStates };
|
|
@@ -571,7 +551,7 @@ export function generateGuiPreviewScenarios(graph, windowName, base, options) {
|
|
|
571
551
|
setIfMissing(values, startValueToken, value);
|
|
572
552
|
}
|
|
573
553
|
for (const key of tokens.numeric) {
|
|
574
|
-
if (!key.endsWith('_bar') ||
|
|
554
|
+
if (!key.endsWith('_bar') || explicitValues[key] !== undefined)
|
|
575
555
|
continue;
|
|
576
556
|
const sourceKey = key.slice(0, -'_bar'.length);
|
|
577
557
|
const sourceValue = values[sourceKey];
|
|
@@ -579,7 +559,7 @@ export function generateGuiPreviewScenarios(graph, windowName, base, options) {
|
|
|
579
559
|
values[key] = sourceValue;
|
|
580
560
|
}
|
|
581
561
|
for (const { value: valueKey, limit: limitKey } of numericConstraints.relationships) {
|
|
582
|
-
if (
|
|
562
|
+
if (explicitValues[valueKey] !== undefined)
|
|
583
563
|
continue;
|
|
584
564
|
const value = values[valueKey];
|
|
585
565
|
const limit = values[limitKey];
|
|
@@ -589,22 +569,27 @@ export function generateGuiPreviewScenarios(graph, windowName, base, options) {
|
|
|
589
569
|
values[valueKey] = Math.max(minimum, Math.min(value, limit));
|
|
590
570
|
}
|
|
591
571
|
for (const key of tokens.textual)
|
|
592
|
-
|
|
572
|
+
if (!scriptedTokens.has(key))
|
|
573
|
+
setIfMissing(values, key, plausibleText(key, random, options.textSamples, scopedCountries));
|
|
593
574
|
const scriptedTextTemplates = new Map();
|
|
594
575
|
for (const key of tokens.textual) {
|
|
595
|
-
|
|
596
|
-
|
|
576
|
+
if (explicitValues[key] !== undefined)
|
|
577
|
+
continue;
|
|
578
|
+
const scriptedValue = scriptedLocalisationValue(graph, key, base.language, 'text', values, base, definitions, conditionResults);
|
|
579
|
+
if (scriptedValue !== undefined)
|
|
597
580
|
scriptedTextTemplates.set(key, scriptedValue);
|
|
598
581
|
}
|
|
599
582
|
for (let pass = 0; pass < 3; pass += 1)
|
|
600
583
|
for (const [key, template] of scriptedTextTemplates)
|
|
601
584
|
values[key] = materializeDynamicText(template, values);
|
|
602
585
|
for (const key of imageTokens) {
|
|
586
|
+
if (explicitValues[key] !== undefined)
|
|
587
|
+
continue;
|
|
603
588
|
if (/(?:^|\.)GetFlag$/u.test(key)) {
|
|
604
589
|
setIfMissing(values, key, generatedCountryTag);
|
|
605
590
|
continue;
|
|
606
591
|
}
|
|
607
|
-
const sprite = scriptedLocalisationValue(graph, key, base.language,
|
|
592
|
+
const sprite = scriptedLocalisationValue(graph, key, base.language, 'image', values, base, definitions, conditionResults);
|
|
608
593
|
if (sprite !== undefined)
|
|
609
594
|
setIfMissing(values, key, sprite);
|
|
610
595
|
}
|
|
@@ -625,7 +610,8 @@ export function generateGuiPreviewScenarios(graph, windowName, base, options) {
|
|
|
625
610
|
return parsePreviewScenario({
|
|
626
611
|
...base,
|
|
627
612
|
id: `${base.id}-${options.idPrefix}-${scenarioIndex + 1}`,
|
|
628
|
-
description: `Generated
|
|
613
|
+
description: `Generated exploratory preview ${scenarioIndex + 1} (seed ${sourceSeed}; not verified campaign state)`,
|
|
614
|
+
conditionResults,
|
|
629
615
|
values,
|
|
630
616
|
country: [...imageTokens].some((key) => /(?:^|\.)GetFlag$/u.test(key))
|
|
631
617
|
? {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { ConditionScopeBindingSchema, ConditionResultSchema } from '../core/condition-schema.js';
|
|
2
3
|
import { renderDimensionViolation, RENDER_MAX_DIMENSION } from '../core/render-budget.js';
|
|
3
4
|
import { ServiceError } from '../core/result.js';
|
|
4
5
|
import { GUI_SCENARIO_MAX_KEYS, GUI_SCENARIO_MAX_LIST_KEYS, GUI_SCENARIO_MAX_ROWS, GUI_SCENARIO_MAX_STRING_CHARACTERS, } from './limits.js';
|
|
@@ -120,6 +121,8 @@ const GuiPreviewScenarioBodySchema = z
|
|
|
120
121
|
visibleTimeSeconds: z.number().min(0).max(86_400).optional(),
|
|
121
122
|
country: objectSchema.optional(),
|
|
122
123
|
stateValues: objectSchema.optional(),
|
|
124
|
+
scopes: z.record(z.string().max(256), ConditionScopeBindingSchema).optional(),
|
|
125
|
+
conditionResults: z.array(ConditionResultSchema).max(10_000).optional(),
|
|
123
126
|
variables: z.record(z.string(), z.number()).default({}),
|
|
124
127
|
flags: z.record(z.string(), z.boolean()).default({}),
|
|
125
128
|
lists: z.record(z.string(), z.array(objectSchema).max(10_000)).default({}),
|
|
@@ -6,6 +6,7 @@ import { compareCodeUnits, canonicalJson, deterministicId, hashCanonical, sha256
|
|
|
6
6
|
import { DiagnosticCollector } from '../core/diagnostic-collector.js';
|
|
7
7
|
import { sortDiagnostics } from '../core/diagnostics.js';
|
|
8
8
|
import { CoreEngine } from '../core/engine.js';
|
|
9
|
+
import { ClausewitzEvaluationDefinitions } from '../core/clausewitz-evaluation.js';
|
|
9
10
|
import { assertRenderDimensions, RenderBudget, RENDER_MAX_PIXELS } from '../core/render-budget.js';
|
|
10
11
|
import { ServiceError } from '../core/result.js';
|
|
11
12
|
import { WorkspaceScanner } from '../core/scanner.js';
|
|
@@ -25,6 +26,8 @@ import { buildGuiSourceGraph } from './source-graph.js';
|
|
|
25
26
|
import { assertGuiSourcePatchesSafe } from './source-patch.js';
|
|
26
27
|
import { validateGuiScene, validateResolutionDrift, validateStateMatrix } from './validators.js';
|
|
27
28
|
const staticGuiDefinitionPatterns = [
|
|
29
|
+
'common/scripted_triggers/**/*.txt',
|
|
30
|
+
'common/script_constants/**/*.txt',
|
|
28
31
|
'common/scripted_localisation/**/*.txt',
|
|
29
32
|
'common/decisions/**/*.txt',
|
|
30
33
|
'common/decision_categories/**/*.txt',
|
|
@@ -814,7 +817,7 @@ export class ScriptedGuiStudio {
|
|
|
814
817
|
const scanned = await this.scanWindow(input.workspaceId, input.windowName, input.principal, input.signal, [placeholderScenario.language, ...explicitRelatedScenarios.map(({ language }) => language)], scenarioSpriteNames([placeholderScenario, ...explicitRelatedScenarios], generatedOptions?.textSamples));
|
|
815
818
|
const generatedScenarios = generatedOptions === undefined
|
|
816
819
|
? []
|
|
817
|
-
: generateGuiPreviewScenarios(scanned.graph, input.windowName, placeholderScenario, generatedOptions);
|
|
820
|
+
: generateGuiPreviewScenarios(scanned.graph, input.windowName, placeholderScenario, generatedOptions, ClausewitzEvaluationDefinitions.build(scanned));
|
|
818
821
|
const scenario = generatedScenarios[0] ?? placeholderScenario;
|
|
819
822
|
const relatedScenarios = [
|
|
820
823
|
...(generatedOptions?.preservePlaceholder === true && generatedScenarios.length > 0
|
|
@@ -893,7 +896,7 @@ export class ScriptedGuiStudio {
|
|
|
893
896
|
], generatedOptions?.textSamples));
|
|
894
897
|
const generatedScenarios = generatedOptions === undefined
|
|
895
898
|
? []
|
|
896
|
-
: generateGuiPreviewScenarios(scanned.graph, input.windowName, placeholderScenario, generatedOptions);
|
|
899
|
+
: generateGuiPreviewScenarios(scanned.graph, input.windowName, placeholderScenario, generatedOptions, ClausewitzEvaluationDefinitions.build(scanned));
|
|
897
900
|
const scenario = generatedScenarios[0] ?? placeholderScenario;
|
|
898
901
|
const relatedScenarios = [
|
|
899
902
|
...(generatedOptions?.preservePlaceholder === true && generatedScenarios.length > 0
|