hoi4-agent-tools 3.0.9 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -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/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/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/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/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/service.js +24 -3
- 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/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 +1 -1
- 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,125 @@
|
|
|
1
|
+
import { publicArtifactLink } from '../core/artifacts.js';
|
|
2
|
+
import { helperExpansionValidation } from '../core/helper-expansion.js';
|
|
3
|
+
import { compareCodeUnits, hashCanonical } from '../core/canonical.js';
|
|
4
|
+
import { emptyServiceResult } from '../core/result.js';
|
|
5
|
+
import { setInlineFilesScanned } from '../core/operation-result.js';
|
|
6
|
+
import { technologyCompareRequestSchema, technologyInspectRequestSchema, technologyRenderRequestSchema, } from '../schemas/technology.js';
|
|
7
|
+
import { technologyDiagnostics } from './service.js';
|
|
8
|
+
export function normalizeTechnologyInspectionRequest(input) {
|
|
9
|
+
return JSON.parse(JSON.stringify(technologyInspectRequestSchema.parse(input)));
|
|
10
|
+
}
|
|
11
|
+
export function normalizeTechnologyRenderRequest(input) {
|
|
12
|
+
return JSON.parse(JSON.stringify(technologyRenderRequestSchema.parse(input)));
|
|
13
|
+
}
|
|
14
|
+
export function normalizeTechnologyCompareRequest(input) {
|
|
15
|
+
return JSON.parse(JSON.stringify(technologyCompareRequestSchema.parse(input)));
|
|
16
|
+
}
|
|
17
|
+
export function technologyGraphHash(graph) {
|
|
18
|
+
return hashCanonical({
|
|
19
|
+
schemaVersion: graph.schemaVersion,
|
|
20
|
+
analysisMode: graph.analysisMode ?? 'full',
|
|
21
|
+
workspaceIdentity: graph.workspaceIdentity,
|
|
22
|
+
revision: graph.revision,
|
|
23
|
+
statistics: graph.statistics,
|
|
24
|
+
sourceHashes: graph.sourceHashes,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function technologyCounts(graph, artifacts) {
|
|
28
|
+
return {
|
|
29
|
+
technologies: graph.statistics.technologyCount,
|
|
30
|
+
legacyDoctrines: graph.statistics.legacyDoctrineCount,
|
|
31
|
+
folders: graph.statistics.folderCount,
|
|
32
|
+
placements: graph.statistics.placementCount,
|
|
33
|
+
edges: graph.edges.length,
|
|
34
|
+
unlocks: graph.statistics.unlockCount,
|
|
35
|
+
references: graph.statistics.externalReferenceCount,
|
|
36
|
+
issues: graph.statistics.issueCount,
|
|
37
|
+
unresolved: graph.statistics.unresolvedCount,
|
|
38
|
+
artifacts,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function technologyValidation(graph) {
|
|
42
|
+
const blocking = technologyDiagnostics(graph).filter(({ severity }) => severity === 'error' || severity === 'blocker').length;
|
|
43
|
+
return {
|
|
44
|
+
passed: graph.complete && blocking === 0,
|
|
45
|
+
checks: [
|
|
46
|
+
{
|
|
47
|
+
id: 'technology-analysis',
|
|
48
|
+
passed: graph.complete && blocking === 0,
|
|
49
|
+
message: graph.complete
|
|
50
|
+
? `${blocking} blocking technology diagnostics; full evidence is linked`
|
|
51
|
+
: graph.analysisMode === 'focused'
|
|
52
|
+
? 'Helper projections were deferred for this large workspace; direct evidence is linked'
|
|
53
|
+
: graph.unresolved.some(({ blockers }) => blockers.some(({ code }) => code === 'TECH_HELPER_DEPTH_BLOCKED' ||
|
|
54
|
+
code === 'TECH_HELPER_PROJECTION_LIMIT'))
|
|
55
|
+
? 'Helper expansion reached a depth or materialization boundary; helper_expansion mode provides bounded source-linked continuation'
|
|
56
|
+
: `${graph.skippedSourceCount} source(s) were skipped; full evidence is linked`,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function inspectTechnologies(viewer, input) {
|
|
62
|
+
const output = await viewer.analyze(input);
|
|
63
|
+
const result = emptyServiceResult(input.workspaceId, {
|
|
64
|
+
mode: input.mode,
|
|
65
|
+
revision: output.graph.revision,
|
|
66
|
+
graphHash: technologyGraphHash(output.graph),
|
|
67
|
+
counts: technologyCounts(output.graph, output.artifacts.length),
|
|
68
|
+
...(output.helperExpansion === undefined ? {} : { helperExpansion: output.helperExpansion }),
|
|
69
|
+
});
|
|
70
|
+
result.code =
|
|
71
|
+
(output.helperExpansion?.complete ?? output.graph.complete)
|
|
72
|
+
? 'TECH_INSPECTED'
|
|
73
|
+
: 'TECH_INSPECTED_PARTIAL';
|
|
74
|
+
setInlineFilesScanned(result, output.graph.filesScanned);
|
|
75
|
+
result.artifacts = output.artifacts.map(publicArtifactLink);
|
|
76
|
+
result.validation = technologyValidation(output.graph);
|
|
77
|
+
if (output.helperExpansion !== undefined)
|
|
78
|
+
result.validation = helperExpansionValidation(output.helperExpansion);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
export async function renderTechnologies(viewer, input) {
|
|
82
|
+
const output = await viewer.renderAndStore(input);
|
|
83
|
+
const result = emptyServiceResult(input.workspaceId, {
|
|
84
|
+
view: output.render.view,
|
|
85
|
+
revision: output.graph.revision,
|
|
86
|
+
graphHash: technologyGraphHash(output.graph),
|
|
87
|
+
hashes: output.render.hashes,
|
|
88
|
+
selectedNodes: output.render.selectedIds.length,
|
|
89
|
+
omittedNodes: output.render.omittedNodeCount,
|
|
90
|
+
focusedRenders: output.focused.length,
|
|
91
|
+
sourceAccurate: output.render.sourceAccurate,
|
|
92
|
+
});
|
|
93
|
+
result.code = output.graph.complete ? 'TECH_RENDERED' : 'TECH_RENDERED_PARTIAL';
|
|
94
|
+
setInlineFilesScanned(result, output.graph.filesScanned);
|
|
95
|
+
result.artifacts = output.artifacts.map(publicArtifactLink);
|
|
96
|
+
result.validation = technologyValidation(output.graph);
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
export async function compareTechnologies(viewer, input) {
|
|
100
|
+
const output = await viewer.compareAndStore(input);
|
|
101
|
+
const comparison = output.comparison;
|
|
102
|
+
const result = emptyServiceResult(input.workspaceId, {
|
|
103
|
+
beforeRevision: comparison.beforeRevision,
|
|
104
|
+
afterRevision: comparison.afterRevision,
|
|
105
|
+
added: comparison.technologies.added.length,
|
|
106
|
+
removed: comparison.technologies.removed.length,
|
|
107
|
+
renamed: comparison.technologies.renamed.length,
|
|
108
|
+
moved: comparison.technologies.moved.length,
|
|
109
|
+
regressions: comparison.regressions.length,
|
|
110
|
+
artifacts: output.artifacts.length,
|
|
111
|
+
...(output.render === undefined ? {} : { renderHashes: output.render.hashes }),
|
|
112
|
+
});
|
|
113
|
+
result.code =
|
|
114
|
+
output.before.complete && output.after.complete ? 'TECH_COMPARED' : 'TECH_COMPARED_PARTIAL';
|
|
115
|
+
setInlineFilesScanned(result, [...new Set([...output.before.filesScanned, ...output.after.filesScanned])].sort(compareCodeUnits));
|
|
116
|
+
result.artifacts = output.artifacts.map(publicArtifactLink);
|
|
117
|
+
result.validation = {
|
|
118
|
+
passed: technologyValidation(output.before).passed && technologyValidation(output.after).passed,
|
|
119
|
+
checks: [
|
|
120
|
+
...technologyValidation(output.before).checks,
|
|
121
|
+
...technologyValidation(output.after).checks,
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { inspectHelperExpansion } from '../core/helper-expansion.js';
|
|
3
|
+
import { technologyHelperInventory } from './helper-expansion.js';
|
|
4
|
+
import { clearSemanticDependencies } from '../core/semantic-dependencies.js';
|
|
2
5
|
import { canonicalJson, compareCodeUnits, hashCanonical, sha256Bytes } from '../core/canonical.js';
|
|
3
6
|
import { boundedSourceHashEvidence, } from '../core/artifacts.js';
|
|
4
7
|
import { RenderBudget } from '../core/render-budget.js';
|
|
@@ -9,7 +12,7 @@ import { PACKAGE_VERSION } from '../version.js';
|
|
|
9
12
|
import { GuiAssetCatalog } from '../gui/assets.js';
|
|
10
13
|
import { buildGuiSourceGraph } from '../gui/source-graph.js';
|
|
11
14
|
import { compareTechnologyGraphs } from './compare.js';
|
|
12
|
-
import { buildTechnologyGraph, technologyAssetPatterns } from './graph.js';
|
|
15
|
+
import { buildTechnologyGraph, buildTechnologyGraphAsync, technologyAssetPatterns, } from './graph.js';
|
|
13
16
|
import { TECHNOLOGY_GRAPH_SCHEMA_VERSION } from './model.js';
|
|
14
17
|
import { analyzeTechnologyImpact, discoverTechnologyFolders, explainTechnology, inspectTechnologyUnlocks, lintTechnologyGraph, technologyBonusCoverage, technologyScanReport, traceTechnology, } from './queries.js';
|
|
15
18
|
import { renderTechnologyGraph, technologyRenderIconSprites, } from './render.js';
|
|
@@ -48,6 +51,7 @@ class BoundedTechnologyFragmentCache {
|
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
53
|
clear() {
|
|
54
|
+
clearSemanticDependencies(this);
|
|
51
55
|
this.#values.clear();
|
|
52
56
|
this.#bytes = 0;
|
|
53
57
|
}
|
|
@@ -445,16 +449,17 @@ export class TechnologyTreeViewer {
|
|
|
445
449
|
const workspace = this.engine.resolver.get(workspaceId, options.principal);
|
|
446
450
|
const generation = this.engine.generation(workspaceId);
|
|
447
451
|
const cached = this.#state.current.get(workspaceId);
|
|
448
|
-
if (options.refresh !== true && cached?.generation === generation)
|
|
449
|
-
return cached.graph;
|
|
450
452
|
const snapshot = await scanTechnologySources(this.engine, workspaceId, workspace, options.principal, options.signal);
|
|
451
453
|
const analysisMode = 'full';
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
454
|
+
const unchangedSource = cached?.generation === generation && cached.snapshot.revision === snapshot.revision;
|
|
455
|
+
const preliminary = unchangedSource
|
|
456
|
+
? cached.graph
|
|
457
|
+
: await buildTechnologyGraphAsync(snapshot, {
|
|
458
|
+
workspaceIdentity: workspace.workspaceIdentity,
|
|
459
|
+
cache: this.#state.fragments,
|
|
460
|
+
analysisMode,
|
|
461
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
462
|
+
});
|
|
458
463
|
const patterns = technologyAssetPatterns(preliminary);
|
|
459
464
|
const assetFiles = patterns.length === 0
|
|
460
465
|
? []
|
|
@@ -462,7 +467,15 @@ export class TechnologyTreeViewer {
|
|
|
462
467
|
patterns,
|
|
463
468
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
464
469
|
});
|
|
465
|
-
const
|
|
470
|
+
const assetIdentity = (files) => hashCanonical(files.map(({ absolutePath, displayPath, loadOrder, sha256 }) => ({
|
|
471
|
+
absolutePath,
|
|
472
|
+
displayPath,
|
|
473
|
+
loadOrder,
|
|
474
|
+
sha256,
|
|
475
|
+
})));
|
|
476
|
+
if (unchangedSource && assetIdentity(assetFiles) === assetIdentity(cached.assetFiles))
|
|
477
|
+
return cached.graph;
|
|
478
|
+
const graph = await buildTechnologyGraphAsync(snapshot, {
|
|
466
479
|
workspaceIdentity: workspace.workspaceIdentity,
|
|
467
480
|
cache: this.#state.fragments,
|
|
468
481
|
assetFiles,
|
|
@@ -474,6 +487,24 @@ export class TechnologyTreeViewer {
|
|
|
474
487
|
return graph;
|
|
475
488
|
}
|
|
476
489
|
async analyze(input) {
|
|
490
|
+
if (input.mode === 'helper_expansion') {
|
|
491
|
+
const workspace = this.engine.resolver.get(input.workspaceId, input.principal);
|
|
492
|
+
const snapshot = await scanTechnologySources(this.engine, input.workspaceId, workspace, input.principal, input.signal);
|
|
493
|
+
const graph = await buildTechnologyGraphAsync(snapshot, {
|
|
494
|
+
workspaceIdentity: workspace.workspaceIdentity,
|
|
495
|
+
cache: this.#state.fragments,
|
|
496
|
+
analysisMode: 'focused',
|
|
497
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
498
|
+
});
|
|
499
|
+
const expansion = await inspectHelperExpansion(this.engine, input.workspaceId, technologyHelperInventory(graph, snapshot.complete && graph.unresolved.every(({ kind }) => kind !== 'partial_source')), input.helperExpansion, input.principal, input.signal);
|
|
500
|
+
return {
|
|
501
|
+
graph,
|
|
502
|
+
report: expansion.report,
|
|
503
|
+
reportJson: expansion.reportJson,
|
|
504
|
+
artifacts: expansion.artifacts,
|
|
505
|
+
helperExpansion: expansion.summary,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
477
508
|
const graph = await this.scan(input.workspaceId, {
|
|
478
509
|
refresh: input.refresh ?? input.mode === 'scan',
|
|
479
510
|
...(input.principal === undefined ? {} : { principal: input.principal }),
|
|
@@ -542,11 +573,37 @@ export class TechnologyTreeViewer {
|
|
|
542
573
|
complete: graph.complete,
|
|
543
574
|
analysisBoundary: graph.analysisBoundary,
|
|
544
575
|
mode: input.mode,
|
|
576
|
+
resources: [
|
|
577
|
+
{
|
|
578
|
+
name: `${safeSlug(`technology-${input.mode}`)}-${graph.revision.slice(0, 12)}.json`,
|
|
579
|
+
mimeType: 'application/json',
|
|
580
|
+
},
|
|
581
|
+
...(input.mode === 'scan'
|
|
582
|
+
? [
|
|
583
|
+
{
|
|
584
|
+
name: `technology-graph-${graph.revision}.json`,
|
|
585
|
+
mimeType: 'application/json',
|
|
586
|
+
},
|
|
587
|
+
]
|
|
588
|
+
: []),
|
|
589
|
+
],
|
|
545
590
|
report,
|
|
546
591
|
})}\n`;
|
|
547
592
|
const workspace = this.engine.resolver.get(input.workspaceId, input.principal);
|
|
593
|
+
const graphHashValue = graphHash(graph);
|
|
548
594
|
const artifact = await this.engine.artifacts.putChunked(workspace, `${safeSlug(`technology-${input.mode}`)}-${graph.revision.slice(0, 12)}.json`, 'application/json', reportJson, technologyProvenance(graph, `technology-${input.mode}`, { mode: input.mode }), `Authoritative Technology Tree Viewer ${input.mode.replaceAll('_', ' ')} report`, input.signal);
|
|
549
|
-
|
|
595
|
+
const graphArtifact = input.mode === 'scan'
|
|
596
|
+
? await this.engine.artifacts.putChunked(workspace, `technology-graph-${graph.revision}.json`, 'application/json', `${canonicalJson({ schemaVersion: 'technology-graph-snapshot.v1', graph })}\n`, technologyProvenance(graph, 'technology-graph-snapshot', {
|
|
597
|
+
revision: graph.revision,
|
|
598
|
+
graphHash: graphHashValue,
|
|
599
|
+
}), 'Revision-addressed Technology Tree Viewer graph snapshot', input.signal)
|
|
600
|
+
: undefined;
|
|
601
|
+
return {
|
|
602
|
+
graph,
|
|
603
|
+
report,
|
|
604
|
+
reportJson,
|
|
605
|
+
artifacts: [artifact, ...(graphArtifact === undefined ? [] : [graphArtifact])],
|
|
606
|
+
};
|
|
550
607
|
}
|
|
551
608
|
async renderAndStore(input) {
|
|
552
609
|
const graph = await this.scan(input.workspaceId, {
|
|
@@ -747,10 +804,27 @@ export class TechnologyTreeViewer {
|
|
|
747
804
|
return graph;
|
|
748
805
|
}
|
|
749
806
|
if (reference?.revision !== undefined) {
|
|
750
|
-
const
|
|
751
|
-
if (
|
|
752
|
-
|
|
753
|
-
|
|
807
|
+
const cached = this.#state.history.get(workspaceId)?.get(reference.revision);
|
|
808
|
+
if (cached !== undefined)
|
|
809
|
+
return cached;
|
|
810
|
+
const workspace = this.engine.resolver.get(workspaceId, principal);
|
|
811
|
+
const names = new Set([
|
|
812
|
+
`technology-graph-${reference.revision}.json`,
|
|
813
|
+
`technology-scan-${reference.revision.slice(0, 12)}.json`,
|
|
814
|
+
]);
|
|
815
|
+
const candidates = (await this.engine.artifacts.list(workspace, signal)).filter(({ name }) => names.has(name));
|
|
816
|
+
for (const artifact of candidates) {
|
|
817
|
+
const selected = parseGraphArtifact(await readLogicalArtifact(this.engine, workspaceId, artifact.uri, principal, signal));
|
|
818
|
+
if (selected.revision === reference.revision &&
|
|
819
|
+
selected.workspaceId === workspaceId &&
|
|
820
|
+
selected.workspaceIdentity === current.workspaceIdentity) {
|
|
821
|
+
rememberGraph(this.#state, selected);
|
|
822
|
+
return selected;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (candidates.length === 0)
|
|
826
|
+
throw new ServiceError('TECH_REVISION_NOT_CACHED', 'Requested technology revision is not cached or retained as a graph artifact');
|
|
827
|
+
throw new ServiceError('TECH_GRAPH_WORKSPACE_MISMATCH', 'Retained technology graph evidence does not match the requested revision and workspace topology');
|
|
754
828
|
}
|
|
755
829
|
return current;
|
|
756
830
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { semanticSourceIdentity } from '../core/semantic-dependencies.js';
|
|
2
3
|
import { compareCodeUnits, deterministicId } from '../core/canonical.js';
|
|
3
4
|
import { assignments, childBlocks, firstScalar, nodeLocation, parseClausewitz, sourcePartialLimitDiagnostics, } from '../core/source/index.js';
|
|
4
5
|
const technologyStructuralFields = new Set([
|
|
@@ -1031,7 +1032,7 @@ export function analyzeTechnologySource(file, context) {
|
|
|
1031
1032
|
return fragment;
|
|
1032
1033
|
}
|
|
1033
1034
|
export function technologySourceFragmentCacheKey(file, catalogFingerprint) {
|
|
1034
|
-
return `${file
|
|
1035
|
+
return `${semanticSourceIdentity(file)}:${catalogFingerprint}`;
|
|
1035
1036
|
}
|
|
1036
1037
|
export function technologySourceFiles(files) {
|
|
1037
1038
|
return files
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
export const PACKAGE_NAME = 'hoi4-agent-tools';
|
|
3
|
-
export const PACKAGE_VERSION = '3.0
|
|
3
|
+
export const PACKAGE_VERSION = '3.1.0';
|
|
4
4
|
export const MCP_PROTOCOL_VERSION = LATEST_PROTOCOL_VERSION;
|
|
5
5
|
export const CONFIG_VERSION = 1;
|
|
6
6
|
export const TRANSACTION_VERSION = 1;
|
package/docs/README.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
- [Setup](setup.md): install the server, configure HOI4 paths, and connect an MCP client.
|
|
4
4
|
- [Event chains](events.md): inspect, trace, lint, render, and compare event chains.
|
|
5
5
|
- [Technology trees](technology.md): inspect, trace, lint, render, and compare technologies and doctrines.
|
|
6
|
+
- [Bounded helper expansion](helper-expansion.md): resume source-linked event and technology helper paths with exact coverage boundaries.
|
|
7
|
+
- [Persistent jobs](jobs.md): run, inspect, cancel, and recover long operations.
|
|
6
8
|
- [AI and MTTH analysis](probability.md): evaluate weighted logic, timing, uncertainty, sequences, and patches.
|
|
7
9
|
- [Focus trees](focus.md): inspect, create, render, and clean national or continuous focus content.
|
|
8
10
|
- [Scripted GUIs](gui.md): inspect linked UI source, render states, and create or repair GUIs.
|
package/docs/development.md
CHANGED
|
@@ -25,6 +25,8 @@ The technology acceptance fixture contains 1,040 project-owned technologies acro
|
|
|
25
25
|
|
|
26
26
|
`npx vitest run tests/integration/mcp-concurrent-workloads.test.ts` tests simultaneous production calls across all six domains over six HTTP sessions and four independent stdio processes, including artifact retrieval and queued cancellation. Execution limits and connection behavior are described in [HTTP](http.md) and the [concurrency decision](https://github.com/klimPaskov/hoi4-agent-tools/blob/main/docs/adr/0026-concurrent-request-execution.md).
|
|
27
27
|
|
|
28
|
+
Persistent-job qualification covers authenticated record publication, request-key conflicts, read-result checkpoints, transaction-journal reconciliation, stopped-owner takeover, native task negotiation, ordinary-call parity, reconnect isolation, rewrite cancellation before source application, and fixed-entry worker execution. The public lifecycle contract is documented in [Persistent jobs and MCP tasks](jobs.md).
|
|
29
|
+
|
|
28
30
|
The probability fixture contains more than 150 weighted blocks, 250 scenarios, and a declared stateful pool. Run `npm run benchmark` after analyzer changes. On the July 22, 2026 reference run, cold indexing took 28 ms, a cached block evaluation 29 ms, the 250-row matrix 85 ms, one million draws 9 ms, 3/12/25-candidate sequence cases 20/2,079/356 ms, and retrieval of a 1.84 MB result 3 ms. Review budgets are 250 ms, 150 ms, 750 ms, 100 ms, 200/10,000/3,000 ms, and 100 ms respectively; accuracy, method metadata, and omitted probability must not be weakened to meet them.
|
|
29
31
|
|
|
30
32
|
Local opt-in tests can also read an installed game and an external mod without copying their sources:
|
package/docs/events.md
CHANGED
|
@@ -6,7 +6,7 @@ Use the three event tools directly from the target mod.
|
|
|
6
6
|
|
|
7
7
|
## Inspect
|
|
8
8
|
|
|
9
|
-
`hoi4.event_inspect` provides
|
|
9
|
+
`hoi4.event_inspect` provides eight focused modes:
|
|
10
10
|
|
|
11
11
|
- `scan`: inventory event definitions, namespaces, call sites, and unresolved references.
|
|
12
12
|
- `roots`: find likely entry points and explain why they are roots.
|
|
@@ -15,8 +15,13 @@ Use the three event tools directly from the target mod.
|
|
|
15
15
|
- `state_flow`: report flags, variables, targets, scopes, and other tracked state read or changed along a route.
|
|
16
16
|
- `lint`: find missing targets, unreachable events, suspicious cycles, conflicting definitions, invalid timing, and unresolved dynamic calls.
|
|
17
17
|
- `impact`: identify callers, descendants, state dependencies, and files affected by a proposed event change.
|
|
18
|
+
- `helper_expansion`: stream bounded source-linked helper-call paths, dispatches, and state accesses with revision-bound continuation.
|
|
18
19
|
|
|
19
|
-
Use narrow identifiers, direction, and depth limits when the task concerns one chain.
|
|
20
|
+
Use narrow identifiers, direction, and depth limits when the task concerns one chain.
|
|
21
|
+
Broad scans build the structural event graph without expanding every scripted helper into duplicate paths.
|
|
22
|
+
Focused trace, path, and selector-based inventory requests may defer workspace-wide helper projections and lifecycle passes; their linked evidence records the partial boundary.
|
|
23
|
+
Focused trace and path calls collapse only the selected bounded route.
|
|
24
|
+
For a large helper closure, use [bounded helper expansion](helper-expansion.md) to receive resumable pages without first materializing every projection.
|
|
20
25
|
|
|
21
26
|
The compact response summarizes the result. Small scan resources contain the complete graph; large scan resources contain exact totals, grouped inventories, representative diagnostics, and the revision needed for focused follow-up queries. Large focused scans keep direct event and on-action evidence, call sites, state accesses, and source locations while leaving helper expansion and workspace-wide lifecycle analysis explicit as deferred boundaries. This keeps an agent's prompt and artifact storage bounded without allowing a broad scan to exhaust the MCP process.
|
|
22
27
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Bounded helper expansion
|
|
2
|
+
|
|
3
|
+
`hoi4.event_inspect` and `hoi4.tech_inspect` accept `mode: "helper_expansion"`.
|
|
4
|
+
This is a read-only stream of source-linked helper paths, not a source rewrite or runtime simulation.
|
|
5
|
+
It avoids materializing the whole helper closure before returning its first page.
|
|
6
|
+
|
|
7
|
+
Start either tool with:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"mode": "helper_expansion",
|
|
12
|
+
"helperExpansion": { "maxRecords": 250, "maxWork": 5000, "maxDepth": 64 }
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If `data.helperExpansion.finished` is false, pass its opaque `continuationUri` to the same tool:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"mode": "helper_expansion",
|
|
21
|
+
"helperExpansion": { "continuationUri": "<data.helperExpansion.continuationUri>" }
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The server restores root selection and depth from the continuation.
|
|
26
|
+
Page size and work limit may change between calls; root selection and depth may not.
|
|
27
|
+
A continuation may be used by a fresh server process with the same configured workspace, authorized principal, tool version, and exact source revision.
|
|
28
|
+
Every resume re-enumerates and verifies source bytes, even if `refresh` is omitted.
|
|
29
|
+
Changed source, domain, root topology, principal, or query is rejected explicitly rather than merged into earlier evidence.
|
|
30
|
+
To analyze edited source or use a different depth, start a new request without the continuation.
|
|
31
|
+
|
|
32
|
+
## Bounds and evidence
|
|
33
|
+
|
|
34
|
+
`helperExpansion` has these optional fields:
|
|
35
|
+
|
|
36
|
+
| Field | Contract |
|
|
37
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
38
|
+
| `rootIds` | Up to 2,000 exact structural helper-call root IDs; omitted means all recognized non-helper callers. Obtain IDs from a page's `rootId` or the linked ordinary scan graph. |
|
|
39
|
+
| `maxDepth` | 1–256 helper calls, default 64. |
|
|
40
|
+
| `maxRecords` | 1–5,000 records per page, default 250. |
|
|
41
|
+
| `maxWork` | 1–100,000 traversal transitions per page, default 5,000. |
|
|
42
|
+
| `continuationUri` | Opaque sibling resource returned in the prior tool result. Do not edit or construct it. |
|
|
43
|
+
|
|
44
|
+
Other inspection filters such as top-level `maxDepth`, `selector`, and `technologyId` are rejected in this mode rather than silently ignored.
|
|
45
|
+
The shared dependency inventory is limited to one million edges; source, parser, and artifact byte limits also remain enforced.
|
|
46
|
+
`maxWork` bounds dependency traversal, not the mandatory source scan or parsing.
|
|
47
|
+
An empty page can still advance work; continue when `finished` is false.
|
|
48
|
+
|
|
49
|
+
The linked JSON contains ordered `records` with `visit`, `terminal`, `cycle`, or `depth` kinds.
|
|
50
|
+
Each record links its root and exact edge-ID path to a page-local `edges` evidence table.
|
|
51
|
+
Event leaves include dispatches and helper-owned state accesses with their conditions, locations, and scope evidence.
|
|
52
|
+
Technology leaves include helper-owned grants, bonuses, and other technology references with source-linked call paths.
|
|
53
|
+
Different structural calls to the same helper remain distinct paths, including parallel calls under different conditions.
|
|
54
|
+
The technology call model links conditions through source locations; it does not evaluate or reconstruct runtime trigger outcomes.
|
|
55
|
+
Ordinary inspect modes remain the route for direct non-helper references and aggregate graph diagnostics.
|
|
56
|
+
|
|
57
|
+
`finished` means every selected root has been visited under the stated cycle and depth policy.
|
|
58
|
+
`complete` additionally requires complete source inventory and zero depth stops.
|
|
59
|
+
Cycles are reported and stopped at the repeated ancestor; completion describes finite simple-path coverage, not recursive execution.
|
|
60
|
+
Dynamic and missing source references remain unresolved findings, with exact counts and at most 100 samples in each page report.
|
|
61
|
+
Completion never establishes that those findings are resolved or that a runtime outcome is known.
|
|
62
|
+
The cumulative record, work, root, cycle, and depth counters describe the entire resumed traversal, while `records` and `work` in the tool summary describe the current page.
|
|
63
|
+
|
|
64
|
+
The page report and its continuation are published together through the shared atomic artifact store.
|
|
65
|
+
The continuation is also linked as a sibling artifact; its resource URI is in the compact tool result, not reconstructed from a filename.
|
|
66
|
+
Cursor storage retains the active ancestor stack rather than completed paths.
|
|
67
|
+
Artifacts follow configured retention and admission limits; an evicted or unavailable cursor fails explicitly and requires a fresh analysis.
|
|
68
|
+
This is not permanent storage of every historical page.
|
|
69
|
+
|
|
70
|
+
## Compatibility and boundaries
|
|
71
|
+
|
|
72
|
+
This opt-in mode and optional result summary are additive in 3.1.0.
|
|
73
|
+
Existing inspection modes retain their own ordering, representative-path policy, and materialization limits.
|
|
74
|
+
A depth- or projection-truncated materialized graph reports incomplete coverage.
|
|
75
|
+
The paged edge-path stream must not be mistaken for the deduplicated projections in those graphs.
|
|
76
|
+
Ordinary calls and persisted jobs return the same page contract.
|
|
77
|
+
Within-page work is bounded; the existing durable result checkpoint handles interruption after a page result has been staged.
|
|
78
|
+
Broader materialized-graph memory refinement remains separate from this bounded page contract.
|
package/docs/http.md
CHANGED
|
@@ -10,6 +10,8 @@ JSON request bodies default to and are capped at 64 MiB, with a shared 128 MiB b
|
|
|
10
10
|
|
|
11
11
|
`maxConcurrentTools` controls expensive operations per server engine (default 2), and `maxSharedTools` controls operations across local processes sharing the same `serverStateRoot` (default 4). Calls waiting for capacity remain cancellable and emit progress when requested. Increase execution capacity only when the host has enough memory for the corresponding scans and renders. HTTP admission, connection, session, and rate limits remain separately configurable.
|
|
12
12
|
|
|
13
|
+
Optional MCP tasks are backed by authenticated state beneath `serverStateRoot`, not by one HTTP connection. A task therefore survives disconnect and reconnect for the same principal, while another principal cannot list, inspect, cancel, or retrieve it. Native rewrites additionally require `hoi4:write` and a stable `requestKey`. See [Persistent jobs and MCP tasks](jobs.md) for retry, cancellation, and retention semantics.
|
|
14
|
+
|
|
13
15
|
Resumable event history defaults to 2 MiB per session within the 16 MiB global store, enough to retain one framed 1 MiB artifact-resource chunk.
|
|
14
16
|
|
|
15
17
|
## Loopback
|
package/docs/jobs.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Persistent jobs and MCP tasks
|
|
2
|
+
|
|
3
|
+
Long-running domain operations support optional MCP tasks while preserving the ordinary tool-call contract.
|
|
4
|
+
Clients that negotiate task support can request a task, disconnect, reconnect with the same authorized identity, inspect or cancel it, and retrieve the original tool result after completion.
|
|
5
|
+
Clients that do not request a task continue to receive the same synchronous tool result as before.
|
|
6
|
+
|
|
7
|
+
Version 3.1.0 routes 2025-era clients through the existing stateful adapter and 2026-07-28 clients through the [modern operation/task adapter](adr/0031-modern-mcp-task-adapter.md) on both stdio and HTTP.
|
|
8
|
+
Modern requests independently negotiate the Tasks extension, use authenticated flat handles, and receive terminal results in `tasks/get`.
|
|
9
|
+
|
|
10
|
+
Clients can request ordinary-call progress with a progress token.
|
|
11
|
+
Both protocol adapters share strictly increasing updates and bounded-frequency heartbeats, with cleanup when the request completes, fails, or is cancelled.
|
|
12
|
+
The modern route uses request-related notifications and automatically streams an HTTP response when a progress update is emitted, while silent calls retain JSON responses.
|
|
13
|
+
A modern native task handoff ends that request's heartbeat without declaring the underlying worker complete; clients retrieve ongoing status messages through `tasks/get`.
|
|
14
|
+
|
|
15
|
+
## Supported tools
|
|
16
|
+
|
|
17
|
+
Optional tasks are available for every read-only event, technology, probability, map, GUI, and focus operation, and for the three rewrite tools:
|
|
18
|
+
|
|
19
|
+
- `hoi4.event_inspect`, `hoi4.event_render`, and `hoi4.event_compare`
|
|
20
|
+
- `hoi4.tech_inspect`, `hoi4.tech_render`, and `hoi4.tech_compare`
|
|
21
|
+
- `hoi4.probability_inspect`, `hoi4.probability_evaluate`, `hoi4.probability_sweep`, `hoi4.probability_simulate`, `hoi4.probability_sequence`, `hoi4.probability_compare`, and `hoi4.probability_render`
|
|
22
|
+
- `hoi4.map_inspect`, `hoi4.map_render`, and `hoi4.map_rewrite`
|
|
23
|
+
- `hoi4.gui_inspect`, `hoi4.gui_render`, and `hoi4.gui_rewrite`
|
|
24
|
+
- `hoi4.focus_inspect`, `hoi4.focus_render`, `hoi4.focus_raster`, and `hoi4.focus_rewrite`
|
|
25
|
+
|
|
26
|
+
Task results are exact tool results, including linked artifacts, partial-analysis markers, validation evidence, and tool-level errors.
|
|
27
|
+
A completed tool-level error is therefore a completed task whose result has `isError: true`; an execution or storage failure is a failed task.
|
|
28
|
+
|
|
29
|
+
## Rewrite request keys
|
|
30
|
+
|
|
31
|
+
A native background rewrite must include a caller-generated `requestKey` between 1 and 256 characters.
|
|
32
|
+
Reuse the same key only for retries of the same rewrite request.
|
|
33
|
+
The key's receipt namespace is scoped to the authenticated principal, workspace identity, and workspace roots, and the receipt binds the original tool, inputs, and tool version.
|
|
34
|
+
Do not reuse a key for a different tool in the same namespace.
|
|
35
|
+
Repeating an identical request with the same key returns the same task and durable result even if task polling or retention hints changed; reusing the key with different rewrite inputs fails explicitly.
|
|
36
|
+
Ordinary synchronous rewrite calls do not require `requestKey`.
|
|
37
|
+
An exact keyed retry after task visibility expires renews the task's polling window and retrieves the saved outcome without applying the rewrite again.
|
|
38
|
+
This also preserves failed and cancelled outcomes; a retry key cannot restart a terminal operation.
|
|
39
|
+
Both native and ordinary calls use that same receipt, while a conflicting retry or status lookup cannot extend its visibility.
|
|
40
|
+
|
|
41
|
+
The server binds an applied rewrite to its authenticated transaction journal before source replacement starts.
|
|
42
|
+
If execution stops after a commit, recovery reads that journal and reconstructs the domain result without planning or applying the rewrite again.
|
|
43
|
+
Blocked and unchanged plans do not bind a transaction because they never mutate source.
|
|
44
|
+
|
|
45
|
+
## Cancellation and recovery
|
|
46
|
+
|
|
47
|
+
Cancellation is durable and cooperative.
|
|
48
|
+
A queued cancellation becomes terminal without starting source work.
|
|
49
|
+
A running cancellation is observed by the isolated worker; if a rewrite has already crossed the commit boundary, reconciliation reports the actual committed or restored outcome instead of inventing a cancellation or replaying the request.
|
|
50
|
+
Cancelling an ordinary synchronous call prevents admission when possible and otherwise requests cooperative cancellation of its durable job.
|
|
51
|
+
Closing the originating connection does not cancel an accepted native task.
|
|
52
|
+
|
|
53
|
+
Read-only jobs stage a source-revision-bound result checkpoint before terminal publication.
|
|
54
|
+
If a worker or server stops after that checkpoint, a later authorized process publishes the retained result without rerunning the operation.
|
|
55
|
+
Event and technology helper expansion also saves an authenticated intermediate frontier with its exact source revision and traversal contract.
|
|
56
|
+
A replacement validates the saved paths, adjacency cursor, graph identity, and checkpoint bytes before continuing.
|
|
57
|
+
Source changes before intermediate-frontier recovery cause a fresh analysis of the current revision; old and new graph evidence are never combined.
|
|
58
|
+
An active host allows at most two replacement attempts after proven worker death, and never retries the same unchanged frontier twice.
|
|
59
|
+
Checkpoint recovery never overrides cancellation or live/unverifiable ownership, and never replays a rewrite.
|
|
60
|
+
Event and technology scan artifacts retain revision-addressed graphs for later comparisons, and bounded responses state their requested depth, node, edge, render, helper-expansion, or candidate coverage.
|
|
61
|
+
Their opt-in `helper_expansion` mode also returns [bounded continuation pages](helper-expansion.md) through both ordinary calls and persistent jobs.
|
|
62
|
+
Unlike automatic recovery inside a single job, an explicit page resume after a source edit is rejected so a caller cannot combine old and new pages.
|
|
63
|
+
|
|
64
|
+
Workers use fixed registered operations in separate Node.js processes.
|
|
65
|
+
Job records contain validated JSON inputs and authenticated state; they cannot select modules, executables, or arbitrary commands.
|
|
66
|
+
Workspace and principal authorization is rechecked for every submission, lookup, list, result retrieval, and cancellation.
|
|
67
|
+
|
|
68
|
+
## Compatibility tools and retention
|
|
69
|
+
|
|
70
|
+
`hoi4.job_inspect` returns bounded status for an active, cancelled, or failed job and returns the original tool result for a completed job.
|
|
71
|
+
`hoi4.job_cancel` records an authorized cancellation request.
|
|
72
|
+
Both are control-plane operations and remain responsive while domain execution slots are occupied.
|
|
73
|
+
Both protocol adapters use the same control service, and these calls remain foreground operations even when a modern caller advertises the Tasks extension.
|
|
74
|
+
Job records are principal-private, including when another principal has access to the same workspace; this differs from artifacts shared within authorized workspace grants.
|
|
75
|
+
|
|
76
|
+
Native task identifiers include the workspace and persistent job identifier.
|
|
77
|
+
They are opaque client values; transaction identifiers and plan hashes are never public workflow inputs.
|
|
78
|
+
New persistent job IDs are derived from private server state, and earlier authenticated retry receipts remain usable without repeating their original operation.
|
|
79
|
+
The modern adapter adds an authenticated handle suffix, including when it references an earlier receipt, so clients must retain the complete task ID rather than reconstructing it from job metadata or reusing a handle from another protocol generation.
|
|
80
|
+
Read-only terminal jobs follow the negotiated task retention period, clamped between one minute and seven days with a 24-hour default.
|
|
81
|
+
Mutation receipts remain durable after task visibility expires so a retry cannot duplicate an uncertain write.
|
|
82
|
+
Renewed task responses report their total lifetime from the original creation time, including elapsed age, while the newly granted visibility window remains bounded.
|
|
83
|
+
|
|
84
|
+
Persistent tasks require an operator-owned `serverStateRoot`.
|
|
85
|
+
HTTP principals can see only jobs within their existing workspace grants, and rewrite submission or cancellation also requires `hoi4:write`.
|
package/docs/technology.md
CHANGED
|
@@ -6,7 +6,7 @@ Use the three technology tools directly from the target mod.
|
|
|
6
6
|
|
|
7
7
|
## Inspect
|
|
8
8
|
|
|
9
|
-
`hoi4.tech_inspect` provides
|
|
9
|
+
`hoi4.tech_inspect` provides nine focused modes:
|
|
10
10
|
|
|
11
11
|
- `scan`: build the technology graph and report its inventory, diagnostics, analysis limits, and revision. Small graphs include the complete graph in the report; large graphs return exact counts, grouped findings, samples, and the revision for focused follow-up queries.
|
|
12
12
|
- `folders`: list folder roots and source placements, or inspect one `folderId`.
|
|
@@ -16,6 +16,7 @@ Use the three technology tools directly from the target mod.
|
|
|
16
16
|
- `bonus_coverage`: show which technologies or categories are covered by discovered research bonuses.
|
|
17
17
|
- `lint`: filter source-linked findings by classification, diagnostic code, folder, or technology.
|
|
18
18
|
- `impact`: find the definitions, placements, edges, grants, bonuses, unlocks, localisation, sprites, and files affected by a proposed removal or rename.
|
|
19
|
+
- `helper_expansion`: stream source-linked scripted-effect call paths and helper-owned technology references with bounded pages and revision-bound continuation.
|
|
19
20
|
|
|
20
21
|
Example trace:
|
|
21
22
|
|
|
@@ -45,7 +46,10 @@ Impact mode takes a subject with `kind` set to `technology`, `category`, `folder
|
|
|
45
46
|
|
|
46
47
|
Findings are separated into confirmed errors, probable defects, design warnings, and unresolved analysis. Roots, grant-only technologies, early dates, zero AI weights, repeated effects, and routing nodes are evaluated in context instead of being treated as automatic defects. The viewer keeps its authoritative graph in memory while the linked scan artifact remains a bounded summary; use its revision with the focused inspect modes or a render resource to inspect the relevant records.
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
Ordinary technology inspection uses the materialized graph and its explicit helper-depth and projection limits.
|
|
50
|
+
A truncated helper expansion reports incomplete coverage.
|
|
51
|
+
For a large helper closure, [bounded helper expansion](helper-expansion.md) uses the structural call inventory and streams source-linked paths without first materializing the full projection.
|
|
52
|
+
It preserves separate conditional call sites and identifies source, cycle, and depth boundaries without simulating trigger outcomes.
|
|
49
53
|
|
|
50
54
|
## Render
|
|
51
55
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hoi4-agent-tools",
|
|
3
3
|
"mcpName": "io.github.klimPaskov/hoi4-agent-tools",
|
|
4
|
-
"version": "3.0
|
|
4
|
+
"version": "3.1.0",
|
|
5
5
|
"description": "Analyze HOI4 events, tech trees, AI weights, and MTTH; build focus trees, GUIs, and maps.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
"docs/map.md",
|
|
48
48
|
"docs/http.md",
|
|
49
49
|
"docs/development.md",
|
|
50
|
+
"docs/jobs.md",
|
|
51
|
+
"docs/helper-expansion.md",
|
|
50
52
|
"docs/images/readme/",
|
|
51
53
|
"examples/clients/",
|
|
52
54
|
"examples/probability/",
|
|
@@ -99,23 +101,27 @@
|
|
|
99
101
|
},
|
|
100
102
|
"dependencies": {
|
|
101
103
|
"@fontsource-variable/roboto": "5.3.0",
|
|
104
|
+
"@modelcontextprotocol/core": "2.0.0",
|
|
105
|
+
"@modelcontextprotocol/node": "2.0.0",
|
|
102
106
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
107
|
+
"@modelcontextprotocol/server": "2.0.0",
|
|
103
108
|
"express": "5.2.1",
|
|
104
109
|
"fast-glob": "3.3.3",
|
|
105
110
|
"fontkit": "2.0.4",
|
|
106
111
|
"iconv-lite": "0.7.3",
|
|
107
112
|
"jose": "6.2.8",
|
|
108
|
-
"sharp": "0.35.
|
|
113
|
+
"sharp": "0.35.4",
|
|
109
114
|
"zod": "4.4.3"
|
|
110
115
|
},
|
|
111
116
|
"devDependencies": {
|
|
112
117
|
"@eslint/js": "10.0.1",
|
|
118
|
+
"@modelcontextprotocol/client": "2.0.0",
|
|
113
119
|
"@modelcontextprotocol/inspector": "2.1.0",
|
|
114
120
|
"@types/express": "5.0.6",
|
|
115
121
|
"@types/fontkit": "2.0.9",
|
|
116
122
|
"@types/node": "22.20.1",
|
|
117
123
|
"@types/pixelmatch": "5.2.6",
|
|
118
|
-
"@vitest/coverage-v8": "4.1.
|
|
124
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
119
125
|
"ajv": "8.20.0",
|
|
120
126
|
"ajv-formats": "3.0.1",
|
|
121
127
|
"eslint": "10.8.1",
|
|
@@ -125,14 +131,14 @@
|
|
|
125
131
|
"tsx": "4.23.11",
|
|
126
132
|
"typescript": "6.0.3",
|
|
127
133
|
"typescript-eslint": "8.66.0",
|
|
128
|
-
"vitest": "4.1.
|
|
134
|
+
"vitest": "4.1.11"
|
|
129
135
|
},
|
|
130
136
|
"overrides": {
|
|
131
137
|
"@hono/node-server": "2.1.0",
|
|
132
138
|
"brace-expansion": "5.0.9",
|
|
133
139
|
"fast-uri": "3.1.7",
|
|
134
140
|
"qs": "6.16.0",
|
|
135
|
-
"hono": "4.13.
|
|
141
|
+
"hono": "4.13.8",
|
|
136
142
|
"nanoid": "3.3.18",
|
|
137
143
|
"ip-address": "10.4.0",
|
|
138
144
|
"postcss": "8.5.26",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.0
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.1.0/schemas/configuration.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|