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
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { boundedSourceHashEvidence, publicArtifactLink } from '../core/artifacts.js';
|
|
3
|
+
import { canonicalJson, compareCodeUnits, hashCanonical } from '../core/canonical.js';
|
|
4
|
+
import { focusDomainScanPatterns } from '../core/domain-scan-patterns.js';
|
|
5
|
+
import { RenderBudget } from '../core/render-budget.js';
|
|
6
|
+
import { emptyServiceResult, ServiceError } from '../core/result.js';
|
|
7
|
+
import { parseClausewitz } from '../core/source/index.js';
|
|
8
|
+
import { autonomousResultArtifacts, } from '../core/transaction-execution.js';
|
|
9
|
+
import { readDependenciesFromScannedFiles, } from '../core/transactions.js';
|
|
10
|
+
import { setInlineFilesScanned } from '../core/operation-result.js';
|
|
11
|
+
import { PACKAGE_VERSION } from '../version.js';
|
|
12
|
+
import { focusPlanHash, focusPresentationEvidence, importContinuousFocusPalettes, assertCompactLayoutQuality, compactFocusTreePlanAsync, linkContinuousFocusPalettes, resolveFocusPresentation, } from './index.js';
|
|
13
|
+
import { activeContinuousFocusFiles, activeFocusFiles, activeSidecar, continuousPalettes, importFocusFile, referenceCatalog, selectContinuousPlan, selectedContinuousFocusFile, selectedFocusFile, selectPlan, underConfiguredRoot, validationFromDiagnostics, } from './tool-support.js';
|
|
14
|
+
const LARGE_FOCUS_RENDER_NODE_THRESHOLD = 200;
|
|
15
|
+
const LARGE_FOCUS_RENDER_SCALE = 0.5;
|
|
16
|
+
function automaticFocusRenderScale(focusCount) {
|
|
17
|
+
return focusCount >= LARGE_FOCUS_RENDER_NODE_THRESHOLD ? LARGE_FOCUS_RENDER_SCALE : 1;
|
|
18
|
+
}
|
|
19
|
+
const jsonObject = z.record(z.string(), z.json());
|
|
20
|
+
const focusRewriteRecipeSchema = z
|
|
21
|
+
.object({
|
|
22
|
+
data: jsonObject,
|
|
23
|
+
filesScanned: z.array(z.string().min(1).max(4096)),
|
|
24
|
+
})
|
|
25
|
+
.strict();
|
|
26
|
+
export async function computeFocusVisualRevisions(engine, context, workspaceId, snapshot, selectors) {
|
|
27
|
+
const workspace = engine.resolver.get(workspaceId, context.principal);
|
|
28
|
+
const paletteImport = continuousPalettes(snapshot);
|
|
29
|
+
const revisions = [];
|
|
30
|
+
for (const selector of selectors) {
|
|
31
|
+
context.signal?.throwIfAborted();
|
|
32
|
+
const imported = importFocusFile(snapshot, selectedFocusFile(snapshot, workspace.registration.roots.focus, selector.relativePath), paletteImport.palettes);
|
|
33
|
+
const plan = selectPlan(imported, selector.treeId);
|
|
34
|
+
const linkedPalettes = paletteImport.palettes.filter(({ id }) => plan.continuousFocusPaletteIds.includes(id));
|
|
35
|
+
const presentation = await resolveFocusPresentation({
|
|
36
|
+
plans: [plan],
|
|
37
|
+
palettes: linkedPalettes,
|
|
38
|
+
files: snapshot.files,
|
|
39
|
+
index: snapshot.index,
|
|
40
|
+
scanner: engine.scanner,
|
|
41
|
+
workspace,
|
|
42
|
+
decodeIcons: false,
|
|
43
|
+
budget: new RenderBudget(),
|
|
44
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
45
|
+
});
|
|
46
|
+
const filesScanned = [
|
|
47
|
+
...new Set([plan.provenance.sourcePath, ...presentation.filesScanned]),
|
|
48
|
+
].sort(compareCodeUnits);
|
|
49
|
+
revisions.push({
|
|
50
|
+
relativePath: selector.relativePath,
|
|
51
|
+
treeId: plan.id,
|
|
52
|
+
revision: hashCanonical({
|
|
53
|
+
planHash: focusPlanHash(plan),
|
|
54
|
+
linkedPalettes,
|
|
55
|
+
presentationSourceHashes: presentation.sourceHashes,
|
|
56
|
+
}),
|
|
57
|
+
filesScanned,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return revisions;
|
|
61
|
+
}
|
|
62
|
+
export function normalizeFocusInspectRequest(input) {
|
|
63
|
+
return input;
|
|
64
|
+
}
|
|
65
|
+
export function normalizeFocusRenderRequest(input) {
|
|
66
|
+
return input;
|
|
67
|
+
}
|
|
68
|
+
export function normalizeFocusRewriteRequest(input) {
|
|
69
|
+
return input;
|
|
70
|
+
}
|
|
71
|
+
function compactDrift(drift) {
|
|
72
|
+
return {
|
|
73
|
+
status: drift.status,
|
|
74
|
+
sourceChanged: drift.sourceChanged,
|
|
75
|
+
planChanged: drift.planChanged,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export async function prepareFocusRewrite(engine, workbench, input, context) {
|
|
79
|
+
const { relativePath, createIfMissing, horizontalSpacing, verticalSpacing, padding, reviewScale, } = input;
|
|
80
|
+
await context.progress(0, 5, 'Validating shared index and current source');
|
|
81
|
+
const workspace = engine.resolver.get(context.workspaceId, context.principal);
|
|
82
|
+
const snapshot = await engine.scan(context.workspaceId, { patterns: focusDomainScanPatterns(workspace) }, context.principal, context.signal);
|
|
83
|
+
if (input.mode === 'continuous') {
|
|
84
|
+
const plan = input.plan;
|
|
85
|
+
if (createIfMissing &&
|
|
86
|
+
(!relativePath.toLowerCase().endsWith('.txt') ||
|
|
87
|
+
!underConfiguredRoot(relativePath, ['common/continuous_focus'])))
|
|
88
|
+
throw new ServiceError('CONTINUOUS_FOCUS_SOURCE_PATH_INVALID', 'New continuous focus sources must be .txt files beneath common/continuous_focus', { relativePath });
|
|
89
|
+
const sourceFile = activeContinuousFocusFiles(snapshot).find((candidate) => candidate.relativePath === relativePath);
|
|
90
|
+
if (sourceFile === undefined && !createIfMissing)
|
|
91
|
+
selectedContinuousFocusFile(snapshot, relativePath);
|
|
92
|
+
if (sourceFile !== undefined && sourceFile.rootKind !== 'mod')
|
|
93
|
+
throw new ServiceError('CONTINUOUS_FOCUS_SOURCE_READ_ONLY', 'Continuous focus changes require a mod-owned source; create a new mod source path instead of shadowing a read-only file', { relativePath, rootKind: sourceFile.rootKind });
|
|
94
|
+
const imported = sourceFile === undefined
|
|
95
|
+
? undefined
|
|
96
|
+
: await workbench.importContinuousPath(context.workspaceId, relativePath, context.principal);
|
|
97
|
+
const currentPlan = imported?.result.continuousFocusPalettes.find(({ id }) => id === plan.id);
|
|
98
|
+
if (currentPlan === undefined && !createIfMissing) {
|
|
99
|
+
if (imported === undefined)
|
|
100
|
+
selectedContinuousFocusFile(snapshot, relativePath);
|
|
101
|
+
else
|
|
102
|
+
selectContinuousPlan(imported.result, plan.id);
|
|
103
|
+
}
|
|
104
|
+
if (currentPlan === undefined && createIfMissing && sourceFile !== undefined)
|
|
105
|
+
throw new ServiceError('CONTINUOUS_FOCUS_CREATE_REQUIRES_NEW_SOURCE', 'Creating a continuous-focus palette requires a new mod source file so unrelated existing source is never repurposed', { relativePath, paletteId: plan.id });
|
|
106
|
+
const renderBudget = new RenderBudget();
|
|
107
|
+
const presentation = await resolveFocusPresentation({
|
|
108
|
+
plans: [],
|
|
109
|
+
palettes: [...(currentPlan === undefined ? [] : [currentPlan]), plan],
|
|
110
|
+
files: snapshot.files,
|
|
111
|
+
index: snapshot.index,
|
|
112
|
+
scanner: engine.scanner,
|
|
113
|
+
workspace,
|
|
114
|
+
decodeIcons: false,
|
|
115
|
+
budget: renderBudget,
|
|
116
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
117
|
+
});
|
|
118
|
+
await context.progress(1, 5, 'Building proposed continuous focus review');
|
|
119
|
+
const proposedPlanHash = focusPlanHash(plan);
|
|
120
|
+
const reviewPlan = {
|
|
121
|
+
...plan,
|
|
122
|
+
provenance: {
|
|
123
|
+
sourcePath: `plan:${plan.id}`,
|
|
124
|
+
sourceHash: proposedPlanHash,
|
|
125
|
+
importedPlanHash: proposedPlanHash,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
const proposed = await workbench.renderContinuousAndStore(context.workspaceId, reviewPlan, {
|
|
129
|
+
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
130
|
+
presentation,
|
|
131
|
+
sourceHashes: presentation.sourceHashes,
|
|
132
|
+
renderProfile: {
|
|
133
|
+
sourceRevision: snapshot.revision,
|
|
134
|
+
proposedPlanHash,
|
|
135
|
+
output: 'vector',
|
|
136
|
+
},
|
|
137
|
+
rasterize: false,
|
|
138
|
+
budget: renderBudget,
|
|
139
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
140
|
+
});
|
|
141
|
+
const reviewArtifacts = proposed.artifacts.map(publicArtifactLink);
|
|
142
|
+
await context.progress(3, 5, 'Preparing the continuous focus rewrite');
|
|
143
|
+
const planned = await workbench.planContinuousChanges({
|
|
144
|
+
workspaceId: context.workspaceId,
|
|
145
|
+
relativePath,
|
|
146
|
+
plan,
|
|
147
|
+
createIfMissing,
|
|
148
|
+
authority: 'plan',
|
|
149
|
+
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
150
|
+
artifacts: reviewArtifacts,
|
|
151
|
+
readDependencies: readDependenciesFromScannedFiles(snapshot.files),
|
|
152
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
153
|
+
});
|
|
154
|
+
await context.progress(4, 5, 'Continuous focus rewrite prepared for journaled application');
|
|
155
|
+
return {
|
|
156
|
+
transaction: planned.transaction,
|
|
157
|
+
recipe: focusRewriteRecipeSchema.parse({
|
|
158
|
+
data: jsonObject.parse({
|
|
159
|
+
mode: 'continuous',
|
|
160
|
+
paletteId: plan.id,
|
|
161
|
+
drift: compactDrift(planned.drift),
|
|
162
|
+
created: planned.drift.status === 'target_missing',
|
|
163
|
+
}),
|
|
164
|
+
filesScanned: [
|
|
165
|
+
...new Set([
|
|
166
|
+
...(sourceFile === undefined ? [] : [sourceFile.displayPath]),
|
|
167
|
+
...presentation.filesScanned,
|
|
168
|
+
]),
|
|
169
|
+
].sort(compareCodeUnits),
|
|
170
|
+
}),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const suppliedPlan = input.plan;
|
|
174
|
+
if (createIfMissing &&
|
|
175
|
+
(!relativePath.toLowerCase().endsWith('.txt') ||
|
|
176
|
+
!underConfiguredRoot(relativePath, workspace.registration.roots.focus)))
|
|
177
|
+
throw new ServiceError('FOCUS_SOURCE_PATH_INVALID', 'New national focus sources must be .txt files beneath a configured focus root', { relativePath });
|
|
178
|
+
const focusFile = activeFocusFiles(snapshot, workspace.registration.roots.focus).find((candidate) => candidate.relativePath === relativePath);
|
|
179
|
+
if (focusFile === undefined && !createIfMissing)
|
|
180
|
+
selectedFocusFile(snapshot, workspace.registration.roots.focus, relativePath);
|
|
181
|
+
if (focusFile !== undefined && focusFile.rootKind !== 'mod')
|
|
182
|
+
throw new ServiceError('FOCUS_SOURCE_READ_ONLY', 'National focus changes require a mod-owned source; create a new mod source path instead of shadowing a read-only file', { relativePath, rootKind: focusFile.rootKind });
|
|
183
|
+
const sidecar = focusFile === undefined ? undefined : activeSidecar(snapshot, focusFile);
|
|
184
|
+
const catalog = referenceCatalog(snapshot);
|
|
185
|
+
const imported = focusFile === undefined
|
|
186
|
+
? undefined
|
|
187
|
+
: await workbench.importPath(context.workspaceId, relativePath, context.principal, sidecar?.sidecar, catalog);
|
|
188
|
+
const paletteImport = continuousPalettes(snapshot);
|
|
189
|
+
if (imported !== undefined)
|
|
190
|
+
imported.result.plans = imported.result.plans.map((current) => linkContinuousFocusPalettes(current, paletteImport.palettes));
|
|
191
|
+
let compactPlanning;
|
|
192
|
+
let compactSourcePlan;
|
|
193
|
+
let plan;
|
|
194
|
+
if (suppliedPlan !== undefined) {
|
|
195
|
+
if (input.layoutMode === 'compact') {
|
|
196
|
+
compactSourcePlan = suppliedPlan;
|
|
197
|
+
compactPlanning = await compactFocusTreePlanAsync(suppliedPlan, {
|
|
198
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
199
|
+
});
|
|
200
|
+
plan = compactPlanning.plan;
|
|
201
|
+
}
|
|
202
|
+
else
|
|
203
|
+
plan = suppliedPlan;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
if (imported === undefined)
|
|
207
|
+
throw new ServiceError('FOCUS_COMPACT_SOURCE_REQUIRED', 'Plan-free compact reflow requires an existing national focus source', { relativePath });
|
|
208
|
+
compactSourcePlan = selectPlan(imported.result, input.treeId);
|
|
209
|
+
compactPlanning = await compactFocusTreePlanAsync(compactSourcePlan, {
|
|
210
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
211
|
+
});
|
|
212
|
+
plan = compactPlanning.plan;
|
|
213
|
+
}
|
|
214
|
+
const currentPlan = imported?.result.plans.find(({ id }) => id === plan.id);
|
|
215
|
+
if (currentPlan === undefined && !createIfMissing) {
|
|
216
|
+
if (imported === undefined)
|
|
217
|
+
selectedFocusFile(snapshot, workspace.registration.roots.focus, relativePath);
|
|
218
|
+
else
|
|
219
|
+
selectPlan(imported.result, plan.id);
|
|
220
|
+
}
|
|
221
|
+
if (currentPlan === undefined && createIfMissing && focusFile !== undefined)
|
|
222
|
+
throw new ServiceError('FOCUS_CREATE_REQUIRES_NEW_SOURCE', 'Creating a national focus tree requires a new mod source file so an existing source and its planning sidecar cannot be repurposed', { relativePath, treeId: plan.id });
|
|
223
|
+
const renderBudget = new RenderBudget();
|
|
224
|
+
const effectiveReviewScale = reviewScale ?? automaticFocusRenderScale(plan.focuses.length);
|
|
225
|
+
const reviewRenderOptions = {
|
|
226
|
+
...(horizontalSpacing === undefined ? {} : { horizontalSpacing }),
|
|
227
|
+
...(verticalSpacing === undefined ? {} : { verticalSpacing }),
|
|
228
|
+
...(padding === undefined ? {} : { padding }),
|
|
229
|
+
outputScale: effectiveReviewScale,
|
|
230
|
+
};
|
|
231
|
+
const reviewRenderProfile = {
|
|
232
|
+
...(horizontalSpacing === undefined ? {} : { horizontalSpacing }),
|
|
233
|
+
...(verticalSpacing === undefined ? {} : { verticalSpacing }),
|
|
234
|
+
...(padding === undefined ? {} : { padding }),
|
|
235
|
+
reviewScale: effectiveReviewScale,
|
|
236
|
+
};
|
|
237
|
+
const presentation = await resolveFocusPresentation({
|
|
238
|
+
plans: [...(currentPlan === undefined ? [] : [currentPlan]), plan],
|
|
239
|
+
palettes: paletteImport.palettes.filter(({ id }) => plan.continuousFocusPaletteIds.includes(id)),
|
|
240
|
+
files: snapshot.files,
|
|
241
|
+
index: snapshot.index,
|
|
242
|
+
scanner: engine.scanner,
|
|
243
|
+
workspace,
|
|
244
|
+
decodeIcons: false,
|
|
245
|
+
budget: renderBudget,
|
|
246
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
247
|
+
});
|
|
248
|
+
const currentLayout = currentPlan === undefined
|
|
249
|
+
? undefined
|
|
250
|
+
: currentPlan === compactSourcePlan && compactPlanning !== undefined
|
|
251
|
+
? compactPlanning.currentLayout
|
|
252
|
+
: await workbench.layoutAsync(currentPlan, {
|
|
253
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
254
|
+
});
|
|
255
|
+
await context.progress(1, 5, 'Building proposed focus review');
|
|
256
|
+
const proposedPlanHash = focusPlanHash(plan);
|
|
257
|
+
const reviewPlan = {
|
|
258
|
+
...plan,
|
|
259
|
+
provenance: {
|
|
260
|
+
sourcePath: `plan:${plan.id}`,
|
|
261
|
+
sourceHash: proposedPlanHash,
|
|
262
|
+
importedPlanHash: proposedPlanHash,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
const proposedLayout = compactPlanning?.proposedLayout ??
|
|
266
|
+
(await workbench.layoutAsync(reviewPlan, {
|
|
267
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
268
|
+
}));
|
|
269
|
+
if (input.layoutMode === 'compact')
|
|
270
|
+
assertCompactLayoutQuality(currentLayout, proposedLayout);
|
|
271
|
+
const proposed = await workbench.renderAndStore(context.workspaceId, reviewPlan, {
|
|
272
|
+
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
273
|
+
index: snapshot.index,
|
|
274
|
+
references: catalog,
|
|
275
|
+
presentation,
|
|
276
|
+
sourceHashes: presentation.sourceHashes,
|
|
277
|
+
...reviewRenderOptions,
|
|
278
|
+
rasterize: false,
|
|
279
|
+
renderProfile: {
|
|
280
|
+
sourceRevision: snapshot.revision,
|
|
281
|
+
proposedPlanHash,
|
|
282
|
+
reviewRenderProfile,
|
|
283
|
+
output: 'vector',
|
|
284
|
+
},
|
|
285
|
+
budget: renderBudget,
|
|
286
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
287
|
+
layout: proposedLayout,
|
|
288
|
+
});
|
|
289
|
+
const reviewArtifacts = proposed.artifacts.map(publicArtifactLink);
|
|
290
|
+
await context.progress(3, 5, 'Preparing the focus-tree rewrite');
|
|
291
|
+
const planned = await workbench.planChanges({
|
|
292
|
+
workspaceId: context.workspaceId,
|
|
293
|
+
relativePath,
|
|
294
|
+
plan,
|
|
295
|
+
createIfMissing,
|
|
296
|
+
authority: 'plan',
|
|
297
|
+
layout: proposed.layout,
|
|
298
|
+
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
299
|
+
index: snapshot.index,
|
|
300
|
+
references: catalog,
|
|
301
|
+
artifacts: reviewArtifacts,
|
|
302
|
+
readDependencies: readDependenciesFromScannedFiles(snapshot.files),
|
|
303
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
304
|
+
});
|
|
305
|
+
await context.progress(4, 5, 'Focus rewrite prepared for journaled application');
|
|
306
|
+
return {
|
|
307
|
+
transaction: planned.transaction,
|
|
308
|
+
recipe: focusRewriteRecipeSchema.parse({
|
|
309
|
+
data: jsonObject.parse({
|
|
310
|
+
mode: 'national',
|
|
311
|
+
treeId: plan.id,
|
|
312
|
+
drift: compactDrift(planned.drift),
|
|
313
|
+
created: planned.drift.status === 'target_missing',
|
|
314
|
+
layoutHash: planned.layout.layoutHash,
|
|
315
|
+
}),
|
|
316
|
+
filesScanned: [
|
|
317
|
+
...new Set([
|
|
318
|
+
...(focusFile === undefined ? [] : [focusFile.displayPath]),
|
|
319
|
+
...presentation.filesScanned,
|
|
320
|
+
]),
|
|
321
|
+
].sort(compareCodeUnits),
|
|
322
|
+
}),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
export function completeFocusRewrite(workspaceId, execution, recipe) {
|
|
326
|
+
const parsed = focusRewriteRecipeSchema.parse(recipe);
|
|
327
|
+
const transaction = execution.transaction;
|
|
328
|
+
const mode = parsed.data.mode;
|
|
329
|
+
const result = emptyServiceResult(workspaceId, {
|
|
330
|
+
...parsed.data,
|
|
331
|
+
execution: execution.outcome,
|
|
332
|
+
fileCount: transaction.files.length,
|
|
333
|
+
artifactCount: transaction.artifacts.length,
|
|
334
|
+
});
|
|
335
|
+
result.status = transaction.validation.passed ? 'ok' : 'blocked';
|
|
336
|
+
result.code =
|
|
337
|
+
mode === 'continuous'
|
|
338
|
+
? execution.outcome === 'applied'
|
|
339
|
+
? 'CONTINUOUS_FOCUS_CHANGES_APPLIED'
|
|
340
|
+
: execution.outcome === 'unchanged'
|
|
341
|
+
? 'CONTINUOUS_FOCUS_CHANGES_UNCHANGED'
|
|
342
|
+
: 'CONTINUOUS_FOCUS_CHANGES_BLOCKED'
|
|
343
|
+
: execution.outcome === 'applied'
|
|
344
|
+
? 'FOCUS_CHANGES_APPLIED'
|
|
345
|
+
: execution.outcome === 'unchanged'
|
|
346
|
+
? 'FOCUS_CHANGES_UNCHANGED'
|
|
347
|
+
: 'FOCUS_CHANGES_BLOCKED';
|
|
348
|
+
setInlineFilesScanned(result, parsed.filesScanned);
|
|
349
|
+
result.proposedFiles = transaction.files.slice(0, 100).map(({ relativePath }) => relativePath);
|
|
350
|
+
result.changedFiles = transaction.appliedFiles.slice(0, 100);
|
|
351
|
+
result.diagnostics = transaction.diagnostics.slice(0, 100);
|
|
352
|
+
result.artifacts = autonomousResultArtifacts(execution);
|
|
353
|
+
result.validation = transaction.validation;
|
|
354
|
+
result.blockers = transaction.diagnostics
|
|
355
|
+
.filter(({ severity }) => severity === 'error' || severity === 'blocker')
|
|
356
|
+
.map(({ code, message, details }) => ({
|
|
357
|
+
code,
|
|
358
|
+
message,
|
|
359
|
+
...(details === undefined ? {} : { details }),
|
|
360
|
+
}));
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
export async function inspectFocus(engine, workbench, input, context) {
|
|
364
|
+
await context.progress(0, 3, 'Building shared workspace index');
|
|
365
|
+
const workspace = engine.resolver.get(context.workspaceId, context.principal);
|
|
366
|
+
const snapshot = await engine.scan(context.workspaceId, { patterns: focusDomainScanPatterns(workspace) }, context.principal, context.signal);
|
|
367
|
+
if (input.mode === 'continuous') {
|
|
368
|
+
const selectedFiles = activeContinuousFocusFiles(snapshot).filter((file) => input.relativePath === undefined || file.relativePath === input.relativePath);
|
|
369
|
+
if (selectedFiles.length === 0)
|
|
370
|
+
throw new ServiceError('CONTINUOUS_FOCUS_SOURCE_NOT_FOUND', 'No active continuous focus source matched the request');
|
|
371
|
+
const diagnostics = [];
|
|
372
|
+
const importedPalettes = [];
|
|
373
|
+
for (const file of selectedFiles) {
|
|
374
|
+
context.signal?.throwIfAborted();
|
|
375
|
+
const imported = importContinuousFocusPalettes(parseClausewitz(file.bytes, file.displayPath));
|
|
376
|
+
importedPalettes.push(...imported.continuousFocusPalettes);
|
|
377
|
+
diagnostics.push(...imported.diagnostics);
|
|
378
|
+
}
|
|
379
|
+
const palettes = importedPalettes.filter(({ id }) => input.paletteId === undefined || id === input.paletteId);
|
|
380
|
+
if (palettes.length === 0)
|
|
381
|
+
throw new ServiceError('CONTINUOUS_FOCUS_PALETTE_NOT_FOUND', input.paletteId === undefined
|
|
382
|
+
? 'The selected source contains no continuous focus palette'
|
|
383
|
+
: `Continuous focus palette was not found: ${input.paletteId}`);
|
|
384
|
+
const paletteDiagnosticCounts = new Map();
|
|
385
|
+
for (const palette of palettes) {
|
|
386
|
+
const lintDiagnostics = workbench.lintContinuous(palette);
|
|
387
|
+
paletteDiagnosticCounts.set(palette.id, lintDiagnostics.length);
|
|
388
|
+
diagnostics.push(...lintDiagnostics);
|
|
389
|
+
}
|
|
390
|
+
const presentation = await resolveFocusPresentation({
|
|
391
|
+
plans: [],
|
|
392
|
+
palettes,
|
|
393
|
+
files: snapshot.files,
|
|
394
|
+
index: snapshot.index,
|
|
395
|
+
scanner: engine.scanner,
|
|
396
|
+
workspace,
|
|
397
|
+
decodeIcons: false,
|
|
398
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
399
|
+
});
|
|
400
|
+
diagnostics.push(...presentation.diagnostics);
|
|
401
|
+
await context.progress(2, 3, 'Writing continuous focus inspection');
|
|
402
|
+
const completeSourceHashes = Object.fromEntries(Object.entries({
|
|
403
|
+
...Object.fromEntries(selectedFiles.map(({ displayPath, sha256 }) => [displayPath, sha256])),
|
|
404
|
+
...presentation.sourceHashes,
|
|
405
|
+
}).sort(([left], [right]) => compareCodeUnits(left, right)));
|
|
406
|
+
const sourceEvidence = boundedSourceHashEvidence(completeSourceHashes);
|
|
407
|
+
const artifact = await engine.artifacts.putChunked(workspace, `focus-inspect.${snapshot.revision.slice(0, 16)}.json`, 'application/json', `${canonicalJson({
|
|
408
|
+
schemaVersion: 1,
|
|
409
|
+
mode: 'continuous',
|
|
410
|
+
revision: snapshot.revision,
|
|
411
|
+
continuousFocusPalettes: palettes,
|
|
412
|
+
presentation: focusPresentationEvidence(presentation),
|
|
413
|
+
sourceHashes: completeSourceHashes,
|
|
414
|
+
diagnostics,
|
|
415
|
+
})}\n`, {
|
|
416
|
+
kind: 'focus-inspect',
|
|
417
|
+
toolVersion: PACKAGE_VERSION,
|
|
418
|
+
schemaVersion: 'focus-inspect.v1',
|
|
419
|
+
sourceHashes: sourceEvidence.sourceHashes,
|
|
420
|
+
metadata: { sourceHashInventory: sourceEvidence.inventory },
|
|
421
|
+
}, 'Continuous focus plans and diagnostics', context.signal);
|
|
422
|
+
const result = emptyServiceResult(context.workspaceId, {
|
|
423
|
+
mode: 'continuous',
|
|
424
|
+
revision: snapshot.revision,
|
|
425
|
+
treeCount: 0,
|
|
426
|
+
paletteCount: palettes.length,
|
|
427
|
+
trees: [],
|
|
428
|
+
palettes: palettes.slice(0, 100).map((palette) => ({
|
|
429
|
+
id: palette.id,
|
|
430
|
+
sourcePath: palette.provenance.sourcePath,
|
|
431
|
+
focusCount: palette.focuses.length,
|
|
432
|
+
diagnosticCount: paletteDiagnosticCounts.get(palette.id) ?? 0,
|
|
433
|
+
})),
|
|
434
|
+
});
|
|
435
|
+
result.code = 'FOCUS_INSPECTED';
|
|
436
|
+
setInlineFilesScanned(result, [
|
|
437
|
+
...new Set([
|
|
438
|
+
...selectedFiles.map(({ displayPath }) => displayPath),
|
|
439
|
+
...presentation.filesScanned,
|
|
440
|
+
]),
|
|
441
|
+
].sort((left, right) => compareCodeUnits(left, right)));
|
|
442
|
+
result.diagnostics = diagnostics.slice(0, 100);
|
|
443
|
+
result.artifacts = [publicArtifactLink(artifact)];
|
|
444
|
+
result.validation = validationFromDiagnostics(diagnostics);
|
|
445
|
+
await context.progress(3, 3, 'Continuous focus inspection complete');
|
|
446
|
+
return { result, sourceRevision: snapshot.revision };
|
|
447
|
+
}
|
|
448
|
+
const selectedFiles = activeFocusFiles(snapshot, workspace.registration.roots.focus).filter((file) => input.relativePath === undefined || file.relativePath === input.relativePath);
|
|
449
|
+
if (selectedFiles.length === 0)
|
|
450
|
+
throw new ServiceError('FOCUS_SOURCE_NOT_FOUND', 'No active focus source matched the request');
|
|
451
|
+
const selectedSidecarFiles = selectedFiles.flatMap((file) => {
|
|
452
|
+
const resolved = activeSidecar(snapshot, file);
|
|
453
|
+
return resolved === undefined ? [] : [resolved.file];
|
|
454
|
+
});
|
|
455
|
+
const importedPlans = [];
|
|
456
|
+
const diagnostics = [];
|
|
457
|
+
const paletteImport = continuousPalettes(snapshot);
|
|
458
|
+
diagnostics.push(...paletteImport.diagnostics);
|
|
459
|
+
for (const file of selectedFiles) {
|
|
460
|
+
context.signal?.throwIfAborted();
|
|
461
|
+
const imported = importFocusFile(snapshot, file, paletteImport.palettes);
|
|
462
|
+
importedPlans.push(...imported.plans);
|
|
463
|
+
diagnostics.push(...imported.diagnostics);
|
|
464
|
+
}
|
|
465
|
+
const plans = importedPlans.filter(({ id }) => input.treeId === undefined || id === input.treeId);
|
|
466
|
+
if (plans.length === 0)
|
|
467
|
+
throw new ServiceError('FOCUS_TREE_NOT_FOUND', input.treeId === undefined
|
|
468
|
+
? 'The selected source contains no national focus tree'
|
|
469
|
+
: `Focus tree was not found: ${input.treeId}`);
|
|
470
|
+
const inspectedPlans = [];
|
|
471
|
+
for (const plan of plans) {
|
|
472
|
+
const layout = await workbench.layoutAsync(plan, {
|
|
473
|
+
...(input.previous === undefined ? {} : { previous: input.previous }),
|
|
474
|
+
...(input.laneSpacing === undefined ? {} : { laneSpacing: input.laneSpacing }),
|
|
475
|
+
...(input.nodeSpacing === undefined ? {} : { nodeSpacing: input.nodeSpacing }),
|
|
476
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
477
|
+
});
|
|
478
|
+
const lintDiagnostics = workbench.lint(plan, {
|
|
479
|
+
index: snapshot.index,
|
|
480
|
+
references: referenceCatalog(snapshot),
|
|
481
|
+
layout,
|
|
482
|
+
});
|
|
483
|
+
diagnostics.push(...lintDiagnostics);
|
|
484
|
+
inspectedPlans.push({ plan, layout, diagnosticCount: lintDiagnostics.length });
|
|
485
|
+
}
|
|
486
|
+
const presentation = await resolveFocusPresentation({
|
|
487
|
+
plans,
|
|
488
|
+
palettes: paletteImport.palettes,
|
|
489
|
+
files: snapshot.files,
|
|
490
|
+
index: snapshot.index,
|
|
491
|
+
scanner: engine.scanner,
|
|
492
|
+
workspace,
|
|
493
|
+
decodeIcons: false,
|
|
494
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
495
|
+
});
|
|
496
|
+
diagnostics.push(...presentation.diagnostics);
|
|
497
|
+
await context.progress(2, 3, 'Writing focus plans, diagnostics, and layouts');
|
|
498
|
+
const completeSourceHashes = Object.fromEntries(Object.entries({
|
|
499
|
+
...Object.fromEntries([...selectedFiles, ...selectedSidecarFiles, ...activeContinuousFocusFiles(snapshot)].map(({ displayPath, sha256 }) => [displayPath, sha256])),
|
|
500
|
+
...presentation.sourceHashes,
|
|
501
|
+
}).sort(([left], [right]) => compareCodeUnits(left, right)));
|
|
502
|
+
const sourceEvidence = boundedSourceHashEvidence(completeSourceHashes);
|
|
503
|
+
const artifact = await engine.artifacts.putChunked(workspace, `focus-inspect.${snapshot.revision.slice(0, 16)}.json`, 'application/json', `${canonicalJson({
|
|
504
|
+
schemaVersion: 1,
|
|
505
|
+
revision: snapshot.revision,
|
|
506
|
+
plans,
|
|
507
|
+
layouts: inspectedPlans.map(({ plan, layout, diagnosticCount }) => ({
|
|
508
|
+
treeId: plan.id,
|
|
509
|
+
layout,
|
|
510
|
+
diagnosticCount,
|
|
511
|
+
})),
|
|
512
|
+
continuousFocusPalettes: paletteImport.palettes,
|
|
513
|
+
presentation: focusPresentationEvidence(presentation),
|
|
514
|
+
sourceHashes: completeSourceHashes,
|
|
515
|
+
diagnostics,
|
|
516
|
+
})}\n`, {
|
|
517
|
+
kind: 'focus-inspect',
|
|
518
|
+
toolVersion: PACKAGE_VERSION,
|
|
519
|
+
schemaVersion: 'focus-inspect.v1',
|
|
520
|
+
sourceHashes: sourceEvidence.sourceHashes,
|
|
521
|
+
metadata: { sourceHashInventory: sourceEvidence.inventory },
|
|
522
|
+
}, 'Focus plans, diagnostics, and stable layout decisions', context.signal);
|
|
523
|
+
const result = emptyServiceResult(context.workspaceId, {
|
|
524
|
+
mode: 'national',
|
|
525
|
+
revision: snapshot.revision,
|
|
526
|
+
treeCount: plans.length,
|
|
527
|
+
paletteCount: paletteImport.palettes.length,
|
|
528
|
+
trees: inspectedPlans.slice(0, 100).map(({ plan, layout, diagnosticCount }) => ({
|
|
529
|
+
id: plan.id,
|
|
530
|
+
sourcePath: plan.provenance.sourcePath,
|
|
531
|
+
focusCount: plan.focuses.length,
|
|
532
|
+
branchCount: plan.branchGroups.length,
|
|
533
|
+
continuousPaletteCount: plan.continuousFocusPaletteIds.length,
|
|
534
|
+
continuousFocusCount: plan.continuousFocusIds.length,
|
|
535
|
+
continuousFocusPosition: plan.continuousFocusPosition ?? null,
|
|
536
|
+
continuousFocusPaletteIds: plan.continuousFocusPaletteIds.slice(0, 100),
|
|
537
|
+
resolvedTitleCount: plan.focuses.filter(({ id }) => presentation.entries[id]?.titleSourceLocation !== undefined).length,
|
|
538
|
+
layoutHash: layout.layoutHash,
|
|
539
|
+
layoutDecisionCount: layout.decisions.length,
|
|
540
|
+
layoutMetrics: layout.metrics,
|
|
541
|
+
diagnosticCount,
|
|
542
|
+
})),
|
|
543
|
+
palettes: [],
|
|
544
|
+
});
|
|
545
|
+
result.code = 'FOCUS_INSPECTED';
|
|
546
|
+
setInlineFilesScanned(result, [
|
|
547
|
+
...new Set([
|
|
548
|
+
...selectedFiles.map(({ displayPath }) => displayPath),
|
|
549
|
+
...selectedSidecarFiles.map(({ displayPath }) => displayPath),
|
|
550
|
+
...activeContinuousFocusFiles(snapshot).map(({ displayPath }) => displayPath),
|
|
551
|
+
...presentation.filesScanned,
|
|
552
|
+
]),
|
|
553
|
+
].sort((left, right) => compareCodeUnits(left, right)));
|
|
554
|
+
result.diagnostics = diagnostics.slice(0, 100);
|
|
555
|
+
result.artifacts = [publicArtifactLink(artifact)];
|
|
556
|
+
result.validation = validationFromDiagnostics(diagnostics);
|
|
557
|
+
await context.progress(3, 3, 'Focus inspection complete');
|
|
558
|
+
return { result, sourceRevision: snapshot.revision };
|
|
559
|
+
}
|
|
560
|
+
export async function renderFocus(engine, workbench, input, context, rasterize) {
|
|
561
|
+
const outputName = rasterize ? 'raster' : 'render';
|
|
562
|
+
await context.progress(0, 4, 'Importing and indexing focus source');
|
|
563
|
+
const workspace = engine.resolver.get(context.workspaceId, context.principal);
|
|
564
|
+
const snapshot = await engine.scan(context.workspaceId, { patterns: focusDomainScanPatterns(workspace) }, context.principal, context.signal);
|
|
565
|
+
await context.progress(1, 4, 'Focus source index complete');
|
|
566
|
+
const renderBudget = new RenderBudget();
|
|
567
|
+
if (input.mode === 'continuous') {
|
|
568
|
+
selectedContinuousFocusFile(snapshot, input.relativePath);
|
|
569
|
+
const imported = await workbench.importContinuousPath(context.workspaceId, input.relativePath, context.principal);
|
|
570
|
+
const plan = selectContinuousPlan(imported.result, input.paletteId);
|
|
571
|
+
const presentation = await resolveFocusPresentation({
|
|
572
|
+
plans: [],
|
|
573
|
+
palettes: [plan],
|
|
574
|
+
files: snapshot.files,
|
|
575
|
+
index: snapshot.index,
|
|
576
|
+
scanner: engine.scanner,
|
|
577
|
+
workspace,
|
|
578
|
+
decodeIcons: rasterize,
|
|
579
|
+
budget: renderBudget,
|
|
580
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
581
|
+
});
|
|
582
|
+
await context.progress(2, 4, `Producing continuous focus ${outputName} artifacts`);
|
|
583
|
+
const rendered = await workbench.renderContinuousAndStore(context.workspaceId, plan, {
|
|
584
|
+
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
585
|
+
presentation,
|
|
586
|
+
sourceHashes: presentation.sourceHashes,
|
|
587
|
+
...(input.columns === undefined ? {} : { columns: input.columns }),
|
|
588
|
+
...(input.padding === undefined ? {} : { padding: input.padding }),
|
|
589
|
+
renderProfile: { sourceRevision: snapshot.revision, output: outputName },
|
|
590
|
+
rasterize,
|
|
591
|
+
budget: renderBudget,
|
|
592
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
593
|
+
});
|
|
594
|
+
const diagnostics = [...imported.result.diagnostics, ...rendered.diagnostics];
|
|
595
|
+
const result = emptyServiceResult(context.workspaceId, {
|
|
596
|
+
mode: 'continuous',
|
|
597
|
+
paletteId: plan.id,
|
|
598
|
+
focusCount: plan.focuses.length,
|
|
599
|
+
hashes: rendered.bundle.hashes,
|
|
600
|
+
width: rendered.bundle.width,
|
|
601
|
+
height: rendered.bundle.height,
|
|
602
|
+
});
|
|
603
|
+
result.code = rasterize ? 'CONTINUOUS_FOCUS_RASTERIZED' : 'CONTINUOUS_FOCUS_RENDERED';
|
|
604
|
+
setInlineFilesScanned(result, [...new Set([plan.provenance.sourcePath, ...presentation.filesScanned])].sort((left, right) => compareCodeUnits(left, right)));
|
|
605
|
+
result.diagnostics = diagnostics.slice(0, 100);
|
|
606
|
+
result.artifacts = rendered.artifacts.map(publicArtifactLink);
|
|
607
|
+
result.validation = validationFromDiagnostics(diagnostics);
|
|
608
|
+
await context.progress(4, 4, `Continuous focus ${outputName} complete`);
|
|
609
|
+
return { result, sourceRevision: snapshot.revision };
|
|
610
|
+
}
|
|
611
|
+
const paletteImport = continuousPalettes(snapshot);
|
|
612
|
+
const imported = importFocusFile(snapshot, selectedFocusFile(snapshot, workspace.registration.roots.focus, input.relativePath), paletteImport.palettes);
|
|
613
|
+
const plan = selectPlan(imported, input.treeId);
|
|
614
|
+
const linkedPalettes = paletteImport.palettes.filter(({ id }) => plan.continuousFocusPaletteIds.includes(id));
|
|
615
|
+
const presentation = await resolveFocusPresentation({
|
|
616
|
+
plans: [plan],
|
|
617
|
+
palettes: linkedPalettes,
|
|
618
|
+
files: snapshot.files,
|
|
619
|
+
index: snapshot.index,
|
|
620
|
+
scanner: engine.scanner,
|
|
621
|
+
workspace,
|
|
622
|
+
decodeIcons: rasterize,
|
|
623
|
+
budget: renderBudget,
|
|
624
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
625
|
+
});
|
|
626
|
+
await context.progress(2, 4, `Producing focus ${outputName} artifacts`);
|
|
627
|
+
const effectiveReviewScale = input.reviewScale ?? automaticFocusRenderScale(plan.focuses.length);
|
|
628
|
+
const rendered = await workbench.renderAndStore(context.workspaceId, plan, {
|
|
629
|
+
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
630
|
+
index: snapshot.index,
|
|
631
|
+
references: referenceCatalog(snapshot),
|
|
632
|
+
presentation,
|
|
633
|
+
sourceHashes: presentation.sourceHashes,
|
|
634
|
+
...(input.horizontalSpacing === undefined
|
|
635
|
+
? {}
|
|
636
|
+
: { horizontalSpacing: input.horizontalSpacing }),
|
|
637
|
+
...(input.verticalSpacing === undefined ? {} : { verticalSpacing: input.verticalSpacing }),
|
|
638
|
+
...(input.padding === undefined ? {} : { padding: input.padding }),
|
|
639
|
+
outputScale: effectiveReviewScale,
|
|
640
|
+
rasterize,
|
|
641
|
+
renderProfile: {
|
|
642
|
+
sourceRevision: snapshot.revision,
|
|
643
|
+
reviewScale: effectiveReviewScale,
|
|
644
|
+
output: outputName,
|
|
645
|
+
},
|
|
646
|
+
budget: renderBudget,
|
|
647
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
648
|
+
});
|
|
649
|
+
const diagnostics = [
|
|
650
|
+
...paletteImport.diagnostics,
|
|
651
|
+
...imported.diagnostics,
|
|
652
|
+
...rendered.diagnostics,
|
|
653
|
+
];
|
|
654
|
+
const result = emptyServiceResult(context.workspaceId, {
|
|
655
|
+
mode: 'national',
|
|
656
|
+
treeId: plan.id,
|
|
657
|
+
layoutHash: rendered.layout.layoutHash,
|
|
658
|
+
hashes: rendered.bundle.hashes,
|
|
659
|
+
width: rendered.bundle.width,
|
|
660
|
+
height: rendered.bundle.height,
|
|
661
|
+
});
|
|
662
|
+
result.code = rasterize ? 'FOCUS_RASTERIZED' : 'FOCUS_RENDERED';
|
|
663
|
+
setInlineFilesScanned(result, [...new Set([plan.provenance.sourcePath, ...presentation.filesScanned])].sort((left, right) => compareCodeUnits(left, right)));
|
|
664
|
+
result.diagnostics = diagnostics.slice(0, 100);
|
|
665
|
+
result.artifacts = rendered.artifacts.map(publicArtifactLink);
|
|
666
|
+
result.validation = validationFromDiagnostics(diagnostics);
|
|
667
|
+
await context.progress(4, 4, `Focus ${outputName} complete`);
|
|
668
|
+
return { result, sourceRevision: snapshot.revision };
|
|
669
|
+
}
|