hoi4-agent-tools 3.0.9 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -1
- package/README.md +10 -2
- package/dist/bin/stdio.js +23 -12
- package/dist/hoi4_agent_tools/core/analysis-checkpoints.js +82 -0
- package/dist/hoi4_agent_tools/core/analysis-evidence.js +9 -0
- package/dist/hoi4_agent_tools/core/analysis-job-operations.js +38 -0
- package/dist/hoi4_agent_tools/core/artifact-resource.js +173 -0
- package/dist/hoi4_agent_tools/core/artifacts.js +84 -2
- package/dist/hoi4_agent_tools/core/condition-evaluator.js +16 -0
- package/dist/hoi4_agent_tools/core/dependency-pages.js +216 -0
- package/dist/hoi4_agent_tools/core/dependency-walk.js +266 -0
- package/dist/hoi4_agent_tools/core/domain-validation.js +188 -0
- package/dist/hoi4_agent_tools/core/engine.js +48 -5
- package/dist/hoi4_agent_tools/core/helper-expansion.js +186 -0
- package/dist/hoi4_agent_tools/core/impact-comparison.js +49 -0
- package/dist/hoi4_agent_tools/core/impact-graph.js +301 -0
- package/dist/hoi4_agent_tools/core/impact-semantic.js +256 -0
- package/dist/hoi4_agent_tools/core/index-segments.js +137 -0
- package/dist/hoi4_agent_tools/core/index.js +47 -4
- package/dist/hoi4_agent_tools/core/job-controls.js +109 -0
- package/dist/hoi4_agent_tools/core/job-executor.js +190 -0
- package/dist/hoi4_agent_tools/core/job-service.js +112 -0
- package/dist/hoi4_agent_tools/core/job-store.js +641 -0
- package/dist/hoi4_agent_tools/core/job-transactions.js +126 -0
- package/dist/hoi4_agent_tools/core/job-worker-host.js +202 -0
- package/dist/hoi4_agent_tools/core/job-worker.js +64 -0
- package/dist/hoi4_agent_tools/core/operation-context.js +31 -0
- package/dist/hoi4_agent_tools/core/operation-result.js +412 -0
- package/dist/hoi4_agent_tools/core/operation-tasks.js +387 -0
- package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +383 -0
- package/dist/hoi4_agent_tools/core/result.js +1 -0
- package/dist/hoi4_agent_tools/core/scanner.js +15 -75
- package/dist/hoi4_agent_tools/core/semantic-dependencies.js +233 -0
- package/dist/hoi4_agent_tools/core/shared-request-capacity.js +43 -11
- package/dist/hoi4_agent_tools/core/source/cache.js +107 -0
- package/dist/hoi4_agent_tools/core/source/parser.js +11 -1
- package/dist/hoi4_agent_tools/core/source-overlay.js +125 -0
- package/dist/hoi4_agent_tools/core/transaction-execution.js +134 -0
- package/dist/hoi4_agent_tools/core/transactions.js +80 -27
- package/dist/hoi4_agent_tools/decision/ai.js +104 -0
- package/dist/hoi4_agent_tools/decision/comparison.js +106 -0
- package/dist/hoi4_agent_tools/decision/cost.js +177 -0
- package/dist/hoi4_agent_tools/decision/evaluation.js +256 -0
- package/dist/hoi4_agent_tools/decision/lifecycle.js +114 -0
- package/dist/hoi4_agent_tools/decision/service.js +172 -0
- package/dist/hoi4_agent_tools/decision/source-inventory.js +182 -0
- package/dist/hoi4_agent_tools/event/graph.js +214 -290
- package/dist/hoi4_agent_tools/event/helper-expansion.js +33 -0
- package/dist/hoi4_agent_tools/event/job-operations.js +51 -0
- package/dist/hoi4_agent_tools/event/operations.js +179 -0
- package/dist/hoi4_agent_tools/event/service.js +67 -15
- package/dist/hoi4_agent_tools/event/source-analysis.js +5 -4
- package/dist/hoi4_agent_tools/focus/job-operations.js +77 -0
- package/dist/hoi4_agent_tools/focus/tool-operations.js +669 -0
- package/dist/hoi4_agent_tools/focus/tool-support.js +123 -0
- package/dist/hoi4_agent_tools/gui/job-operations.js +81 -0
- package/dist/hoi4_agent_tools/gui/tool-operations.js +270 -0
- package/dist/hoi4_agent_tools/impact/scenario-suites.js +74 -0
- package/dist/hoi4_agent_tools/impact/service.js +127 -0
- package/dist/hoi4_agent_tools/map/job-operations.js +76 -0
- package/dist/hoi4_agent_tools/map/tool-operations.js +493 -0
- package/dist/hoi4_agent_tools/mcp/prompts/probability.js +25 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register-modern.js +29 -0
- package/dist/hoi4_agent_tools/mcp/resources/register-modern.js +34 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js +4 -150
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +3 -219
- package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +188 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js +18 -6
- package/dist/hoi4_agent_tools/mcp/server/instructions.js +2 -0
- package/dist/hoi4_agent_tools/mcp/server/job-task-store.js +67 -0
- package/dist/hoi4_agent_tools/mcp/server/legacy-task-tools.js +20 -0
- package/dist/hoi4_agent_tools/mcp/server/modern-tasks.js +168 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js +17 -15
- package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +45 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js +2 -412
- package/dist/hoi4_agent_tools/mcp/server/task-request-context.js +9 -0
- package/dist/hoi4_agent_tools/mcp/server/task-tool-catalog.js +16 -0
- package/dist/hoi4_agent_tools/mcp/server/task-tool-definition.js +1 -0
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +1 -123
- package/dist/hoi4_agent_tools/mcp/tools/analysis.js +65 -0
- package/dist/hoi4_agent_tools/mcp/tools/chaosx.js +38 -21
- package/dist/hoi4_agent_tools/mcp/tools/event.js +24 -315
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +34 -1039
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +19 -504
- package/dist/hoi4_agent_tools/mcp/tools/job.js +46 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js +21 -600
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +39 -463
- package/dist/hoi4_agent_tools/mcp/tools/technology.js +40 -304
- package/dist/hoi4_agent_tools/mcp/transports/http.js +58 -1
- package/dist/hoi4_agent_tools/mcp/transports/modern-task-routing.js +109 -0
- package/dist/hoi4_agent_tools/mcp/transports/negotiated-stdio.js +121 -0
- package/dist/hoi4_agent_tools/probability/job-operations.js +58 -0
- package/dist/hoi4_agent_tools/probability/operations.js +183 -0
- package/dist/hoi4_agent_tools/probability/service.js +24 -3
- package/dist/hoi4_agent_tools/schemas/analysis.js +228 -0
- package/dist/hoi4_agent_tools/schemas/common.js +2 -0
- package/dist/hoi4_agent_tools/schemas/event.js +72 -1
- package/dist/hoi4_agent_tools/schemas/focus-requests.js +189 -0
- package/dist/hoi4_agent_tools/schemas/gui-requests.js +193 -0
- package/dist/hoi4_agent_tools/schemas/helper-expansion.js +58 -0
- package/dist/hoi4_agent_tools/schemas/map-requests.js +85 -0
- package/dist/hoi4_agent_tools/schemas/probability-requests.js +168 -0
- package/dist/hoi4_agent_tools/schemas/technology.js +90 -1
- package/dist/hoi4_agent_tools/schemas/transaction.js +1 -0
- package/dist/hoi4_agent_tools/technology/graph.js +83 -78
- package/dist/hoi4_agent_tools/technology/helper-expansion.js +33 -0
- package/dist/hoi4_agent_tools/technology/job-operations.js +60 -0
- package/dist/hoi4_agent_tools/technology/operations.js +125 -0
- package/dist/hoi4_agent_tools/technology/service.js +89 -15
- package/dist/hoi4_agent_tools/technology/source-analysis.js +2 -1
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +3 -0
- package/docs/analysis.md +100 -0
- package/docs/development.md +2 -0
- package/docs/events.md +7 -2
- package/docs/helper-expansion.md +78 -0
- package/docs/http.md +2 -0
- package/docs/jobs.md +86 -0
- package/docs/technology.md +6 -2
- package/package.json +15 -6
- package/schemas/configuration.schema.json +1 -1
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/decision-inspect-input.schema.json +290 -0
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/helper-expansion-request.schema.json +44 -0
- package/schemas/helper-expansion-summary.schema.json +70 -0
- package/schemas/impact-inspect-input.schema.json +203 -0
- package/schemas/map-operation.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +1 -1
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/server.json +3 -3
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { compareCodeUnits, hashCanonical } from '../core/canonical.js';
|
|
2
2
|
import { sortDiagnostics } from '../core/diagnostics.js';
|
|
3
3
|
import { ServiceError } from '../core/result.js';
|
|
4
|
-
import {
|
|
4
|
+
import { beginSemanticAnalysis, semanticFragment, SemanticCatalog, } from '../core/semantic-dependencies.js';
|
|
5
|
+
import { runDependencyAnalysis, runDependencyAnalysisAsync, } from '../core/dependency-walk.js';
|
|
6
|
+
import { EVENT_GRAPH_MAX_EDGES, EVENT_GRAPH_MAX_HELPER_DEPTH, EVENT_GRAPH_MAX_HELPER_PROJECTIONS, EVENT_GRAPH_MAX_HELPER_STATE_PROJECTIONS, EVENT_GRAPH_MAX_ISSUES, EVENT_GRAPH_MAX_NODES, EVENT_GRAPH_MAX_STATE_ACCESSES, EVENT_GRAPH_MAX_STATE_LINK_CANDIDATES, EVENT_GRAPH_MAX_STATE_LINKS, EVENT_GRAPH_MAX_UNRESOLVED, EVENT_GRAPH_WORK_LIMIT, EventAnalysisBudget, } from './limits.js';
|
|
5
7
|
import { EVENT_GRAPH_PARSER_VERSION, EVENT_GRAPH_SCHEMA_VERSION, } from './model.js';
|
|
6
8
|
import { iterativeStronglyConnectedComponents } from './scc.js';
|
|
7
9
|
import { analyzeEventSource, eventSemanticFragmentCacheKey } from './source-analysis.js';
|
|
@@ -338,297 +340,209 @@ function helperName(node) {
|
|
|
338
340
|
return name;
|
|
339
341
|
return node.label || node.id.replace(/^helper:/u, '');
|
|
340
342
|
}
|
|
341
|
-
function
|
|
343
|
+
function helperWalkInput(graph, revision) {
|
|
342
344
|
const nodes = nodeById(graph.nodes);
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
for (const edge of graph.edges) {
|
|
346
|
-
const values = outgoing.get(edge.from) ?? [];
|
|
347
|
-
values.push(edge);
|
|
348
|
-
outgoing.set(edge.from, values);
|
|
349
|
-
}
|
|
350
|
-
for (const values of outgoing.values()) {
|
|
351
|
-
values.sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
352
|
-
}
|
|
353
|
-
const starts = graph.edges
|
|
354
|
-
.filter((edge) => !edge.derived &&
|
|
355
|
-
nodes.get(edge.to)?.kind === 'helper' &&
|
|
356
|
-
nodes.get(edge.from)?.kind !== 'helper')
|
|
345
|
+
const edges = graph.edges
|
|
346
|
+
.filter(({ derived }) => !derived)
|
|
357
347
|
.sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
348
|
+
return {
|
|
349
|
+
domain: 'event-helper-projections',
|
|
350
|
+
sourceRevision: revision,
|
|
351
|
+
edges: edges.map(({ id, from, to }) => ({ id, from, to })),
|
|
352
|
+
roots: edges
|
|
353
|
+
.filter((edge) => nodes.get(edge.to)?.kind === 'helper' && nodes.get(edge.from)?.kind !== 'helper')
|
|
354
|
+
.map(({ id }) => id),
|
|
355
|
+
branches: graph.nodes
|
|
356
|
+
.filter(({ kind }) => kind === 'helper')
|
|
357
|
+
.map(({ id }) => id)
|
|
358
|
+
.sort(compareCodeUnits),
|
|
359
|
+
maximumDepth: EVENT_GRAPH_MAX_HELPER_DEPTH,
|
|
360
|
+
maximumSteps: EVENT_GRAPH_WORK_LIMIT,
|
|
361
|
+
visitPolicy: 'node',
|
|
362
|
+
reverseEdges: true,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function helperCallerScope(nodes, parents, start) {
|
|
366
|
+
const caller = nodes.get(parents.get(start.from) ?? start.from);
|
|
367
|
+
const candidate = start.scope?.source ?? caller?.metadata.expectedScope ?? caller?.metadata.scope;
|
|
368
|
+
return candidate === 'country' ||
|
|
369
|
+
candidate === 'state' ||
|
|
370
|
+
candidate === 'unit_leader' ||
|
|
371
|
+
candidate === 'operative' ||
|
|
372
|
+
candidate === 'character' ||
|
|
373
|
+
candidate === 'global'
|
|
374
|
+
? candidate
|
|
375
|
+
: 'unknown';
|
|
376
|
+
}
|
|
377
|
+
function helperProjections(graph, budget, walk) {
|
|
378
|
+
const edges = new Map(graph.edges.map((edge) => [edge.id, edge]));
|
|
379
|
+
const nodes = nodeById(graph.nodes);
|
|
358
380
|
const projected = [];
|
|
381
|
+
const parents = optionParents(graph.edges);
|
|
359
382
|
const projectedKeys = new Set();
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
for (const
|
|
363
|
-
|
|
364
|
-
if (
|
|
383
|
+
const reported = new Set();
|
|
384
|
+
const callerScopes = new Map();
|
|
385
|
+
for (const record of walk.state.records) {
|
|
386
|
+
budget.spend('helper_projection');
|
|
387
|
+
if (record.kind === 'visit')
|
|
365
388
|
continue;
|
|
366
|
-
const
|
|
367
|
-
const
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if (frame.helperStack.length >= EVENT_GRAPH_MAX_HELPER_DEPTH) {
|
|
418
|
-
const depthKey = `${start.id}:${target.id}`;
|
|
419
|
-
if (!reportedDepth.has(depthKey)) {
|
|
420
|
-
reportedDepth.add(depthKey);
|
|
421
|
-
addIssue(graph.issues, {
|
|
422
|
-
code: 'EVENT_HELPER_DEPTH_LIMIT',
|
|
423
|
-
classification: 'unresolved_analysis',
|
|
424
|
-
severity: 'blocker',
|
|
425
|
-
message: 'Scripted-effect expansion reached the fixed nesting ceiling.',
|
|
426
|
-
confidence: 'unresolved',
|
|
427
|
-
location: edge.location,
|
|
428
|
-
blockers: [
|
|
429
|
-
{
|
|
430
|
-
code: 'HELPER_DEPTH_LIMIT',
|
|
431
|
-
message: 'The helper chain is deeper than the supported static boundary.',
|
|
432
|
-
location: edge.location,
|
|
433
|
-
details: { maximumDepth: EVENT_GRAPH_MAX_HELPER_DEPTH },
|
|
434
|
-
},
|
|
435
|
-
],
|
|
436
|
-
subjectIds: [start.from, target.id],
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
// The expanded structural graph retains every alternate helper
|
|
442
|
-
// route. The collapsed projection needs one deterministic proof per
|
|
443
|
-
// call site and reachable helper, otherwise diamond-shaped helper
|
|
444
|
-
// graphs grow exponentially on real vanilla workspaces.
|
|
445
|
-
if (reachedHelpers.has(target.id))
|
|
446
|
-
continue;
|
|
447
|
-
reachedHelpers.add(target.id);
|
|
448
|
-
stack.push({
|
|
449
|
-
currentId: target.id,
|
|
450
|
-
helperStack: [...frame.helperStack, helperName(target)],
|
|
451
|
-
edgeStack: [...frame.edgeStack, edge],
|
|
452
|
-
visited: new Set([...frame.visited, target.id]),
|
|
453
|
-
});
|
|
454
|
-
continue;
|
|
455
|
-
}
|
|
456
|
-
const structural = [...frame.edgeStack, edge];
|
|
457
|
-
const final = structural.at(-1);
|
|
458
|
-
const projectionKey = `${start.id}\0${edge.to}`;
|
|
459
|
-
if (projectedKeys.has(projectionKey))
|
|
460
|
-
continue;
|
|
461
|
-
projectedKeys.add(projectionKey);
|
|
462
|
-
if (projected.length >= EVENT_GRAPH_MAX_HELPER_PROJECTIONS) {
|
|
463
|
-
addIssue(graph.issues, {
|
|
464
|
-
code: 'EVENT_HELPER_PROJECTION_LIMIT',
|
|
465
|
-
classification: 'unresolved_analysis',
|
|
466
|
-
severity: 'blocker',
|
|
467
|
-
message: 'Collapsed helper projection reached its materialization ceiling; structural helper calls remain available.',
|
|
468
|
-
confidence: 'unresolved',
|
|
389
|
+
const structural = walk.path(record.path).map(({ id }) => edges.get(id));
|
|
390
|
+
const start = structural[0];
|
|
391
|
+
const final = structural.at(-1);
|
|
392
|
+
const helpers = structural.slice(0, -1).map(({ to }) => nodes.get(to));
|
|
393
|
+
if (record.kind === 'cycle' || record.kind === 'depth') {
|
|
394
|
+
const key = `${record.kind}:${start.id}:${final.to}`;
|
|
395
|
+
if (reported.has(key))
|
|
396
|
+
continue;
|
|
397
|
+
reported.add(key);
|
|
398
|
+
const cycle = record.kind === 'cycle';
|
|
399
|
+
addIssue(graph.issues, {
|
|
400
|
+
code: cycle ? 'EVENT_HELPER_CYCLE' : 'EVENT_HELPER_DEPTH_LIMIT',
|
|
401
|
+
classification: 'unresolved_analysis',
|
|
402
|
+
severity: cycle ? 'warning' : 'blocker',
|
|
403
|
+
message: cycle
|
|
404
|
+
? 'A scripted-effect cycle prevents complete helper expansion.'
|
|
405
|
+
: 'Scripted-effect expansion reached the fixed nesting ceiling.',
|
|
406
|
+
confidence: 'unresolved',
|
|
407
|
+
location: final.location,
|
|
408
|
+
blockers: [
|
|
409
|
+
{
|
|
410
|
+
code: cycle ? 'HELPER_CYCLE' : 'HELPER_DEPTH_LIMIT',
|
|
411
|
+
message: cycle
|
|
412
|
+
? 'Static expansion stopped at the repeated scripted effect.'
|
|
413
|
+
: 'The helper chain is deeper than the supported static boundary.',
|
|
414
|
+
location: final.location,
|
|
415
|
+
...(cycle ? {} : { details: { maximumDepth: EVENT_GRAPH_MAX_HELPER_DEPTH } }),
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
subjectIds: cycle
|
|
419
|
+
? [start.from, ...helpers.map(({ id }) => id), final.to]
|
|
420
|
+
: [start.from, final.to],
|
|
421
|
+
});
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const projectionKey = `${start.id}\0${final.to}`;
|
|
425
|
+
if (projectedKeys.has(projectionKey))
|
|
426
|
+
continue;
|
|
427
|
+
projectedKeys.add(projectionKey);
|
|
428
|
+
if (projected.length >= EVENT_GRAPH_MAX_HELPER_PROJECTIONS) {
|
|
429
|
+
addIssue(graph.issues, {
|
|
430
|
+
code: 'EVENT_HELPER_PROJECTION_LIMIT',
|
|
431
|
+
classification: 'unresolved_analysis',
|
|
432
|
+
severity: 'blocker',
|
|
433
|
+
message: 'Collapsed helper projection reached its materialization ceiling; structural helper calls remain available.',
|
|
434
|
+
confidence: 'unresolved',
|
|
435
|
+
location: start.location,
|
|
436
|
+
blockers: [
|
|
437
|
+
{
|
|
438
|
+
code: 'HELPER_PROJECTION_LIMIT',
|
|
439
|
+
message: 'Use a bounded helper-expanded trace for paths omitted from the collapsed workspace graph.',
|
|
469
440
|
location: start.location,
|
|
470
|
-
|
|
471
|
-
{
|
|
472
|
-
code: 'HELPER_PROJECTION_LIMIT',
|
|
473
|
-
message: 'Use a bounded helper-expanded trace for paths omitted from the collapsed workspace graph.',
|
|
474
|
-
location: start.location,
|
|
475
|
-
details: { maximum: EVENT_GRAPH_MAX_HELPER_PROJECTIONS },
|
|
476
|
-
},
|
|
477
|
-
],
|
|
478
|
-
subjectIds: [start.from, start.to],
|
|
479
|
-
});
|
|
480
|
-
return stableUnique(projected);
|
|
481
|
-
}
|
|
482
|
-
projected.push({
|
|
483
|
-
id: `edge:helper-projection:${hashCanonical({
|
|
484
|
-
from: start.from,
|
|
485
|
-
to: edge.to,
|
|
486
|
-
edges: structural.map(({ id }) => id),
|
|
487
|
-
}).slice(0, 32)}`,
|
|
488
|
-
from: start.from,
|
|
489
|
-
to: edge.to,
|
|
490
|
-
reason: 'scripted_effect_expansion',
|
|
491
|
-
conditions: structural.flatMap(({ conditions }) => conditions),
|
|
492
|
-
helperStack: frame.helperStack,
|
|
493
|
-
location: start.location,
|
|
494
|
-
provenance: structural.flatMap(({ provenance }) => provenance),
|
|
495
|
-
confidence: worstConfidence(...structural.map(({ confidence }) => confidence)),
|
|
496
|
-
derived: true,
|
|
497
|
-
...(final.timing === undefined ? {} : { timing: final.timing }),
|
|
498
|
-
...(final.weight === undefined ? {} : { weight: final.weight }),
|
|
499
|
-
...(final.scope === undefined
|
|
500
|
-
? {}
|
|
501
|
-
: {
|
|
502
|
-
scope: final.scope.source === 'unknown' && callerScope !== 'unknown'
|
|
503
|
-
? { ...final.scope, source: callerScope, confidence: 'high' }
|
|
504
|
-
: final.scope,
|
|
505
|
-
}),
|
|
506
|
-
metadata: {
|
|
507
|
-
structuralEdgeIds: structural.map(({ id }) => id),
|
|
508
|
-
terminalDispatchLocation: locationKey(final.location),
|
|
441
|
+
details: { maximum: EVENT_GRAPH_MAX_HELPER_PROJECTIONS },
|
|
509
442
|
},
|
|
510
|
-
|
|
511
|
-
|
|
443
|
+
],
|
|
444
|
+
subjectIds: [start.from, start.to],
|
|
445
|
+
});
|
|
446
|
+
break;
|
|
512
447
|
}
|
|
448
|
+
let callerScope = callerScopes.get(start.id);
|
|
449
|
+
if (callerScope === undefined) {
|
|
450
|
+
callerScope = helperCallerScope(nodes, parents, start);
|
|
451
|
+
callerScopes.set(start.id, callerScope);
|
|
452
|
+
}
|
|
453
|
+
projected.push({
|
|
454
|
+
id: `edge:helper-projection:${hashCanonical({ from: start.from, to: final.to, edges: structural.map(({ id }) => id) }).slice(0, 32)}`,
|
|
455
|
+
from: start.from,
|
|
456
|
+
to: final.to,
|
|
457
|
+
reason: 'scripted_effect_expansion',
|
|
458
|
+
conditions: structural.flatMap(({ conditions }) => conditions),
|
|
459
|
+
helperStack: helpers.map(helperName),
|
|
460
|
+
location: start.location,
|
|
461
|
+
provenance: structural.flatMap(({ provenance }) => provenance),
|
|
462
|
+
confidence: worstConfidence(...structural.map(({ confidence }) => confidence)),
|
|
463
|
+
derived: true,
|
|
464
|
+
...(final.timing === undefined ? {} : { timing: final.timing }),
|
|
465
|
+
...(final.weight === undefined ? {} : { weight: final.weight }),
|
|
466
|
+
...(final.scope === undefined
|
|
467
|
+
? {}
|
|
468
|
+
: {
|
|
469
|
+
scope: final.scope.source === 'unknown' && callerScope !== 'unknown'
|
|
470
|
+
? { ...final.scope, source: callerScope, confidence: 'high' }
|
|
471
|
+
: final.scope,
|
|
472
|
+
}),
|
|
473
|
+
metadata: {
|
|
474
|
+
structuralEdgeIds: structural.map(({ id }) => id),
|
|
475
|
+
terminalDispatchLocation: locationKey(final.location),
|
|
476
|
+
},
|
|
477
|
+
});
|
|
513
478
|
}
|
|
514
479
|
return stableUnique(projected);
|
|
515
480
|
}
|
|
516
|
-
function projectHelperStateAccesses(graph, budget) {
|
|
481
|
+
function projectHelperStateAccesses(graph, budget, walk) {
|
|
517
482
|
const nodes = nodeById(graph.nodes);
|
|
518
|
-
const
|
|
483
|
+
const edges = new Map(graph.edges.map((edge) => [edge.id, edge]));
|
|
519
484
|
const accesses = new Map();
|
|
485
|
+
const parents = optionParents(graph.edges);
|
|
520
486
|
for (const access of graph.stateAccesses) {
|
|
521
487
|
const values = accesses.get(access.ownerId) ?? [];
|
|
522
488
|
values.push(access);
|
|
523
489
|
accesses.set(access.ownerId, values);
|
|
524
490
|
}
|
|
525
|
-
const outgoing = new Map();
|
|
526
|
-
for (const edge of graph.edges) {
|
|
527
|
-
if (edge.derived)
|
|
528
|
-
continue;
|
|
529
|
-
const values = outgoing.get(edge.from) ?? [];
|
|
530
|
-
values.push(edge);
|
|
531
|
-
outgoing.set(edge.from, values);
|
|
532
|
-
}
|
|
533
|
-
for (const values of outgoing.values())
|
|
534
|
-
values.sort((a, b) => compareCodeUnits(a.id, b.id));
|
|
535
|
-
const starts = graph.edges
|
|
536
|
-
.filter((edge) => !edge.derived &&
|
|
537
|
-
nodes.get(edge.to)?.kind === 'helper' &&
|
|
538
|
-
nodes.get(edge.from)?.kind !== 'helper')
|
|
539
|
-
.sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
540
491
|
const projected = [];
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
492
|
+
const projectedKeys = new Set();
|
|
493
|
+
const callerScopes = new Map();
|
|
494
|
+
for (const record of walk.state.records) {
|
|
495
|
+
if (record.kind !== 'visit')
|
|
544
496
|
continue;
|
|
545
|
-
|
|
546
|
-
const
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const stack = [
|
|
558
|
-
{
|
|
559
|
-
helperId: first.id,
|
|
560
|
-
helperStack: [helperName(first)],
|
|
561
|
-
callEdges: [start],
|
|
562
|
-
visited: new Set([first.id]),
|
|
563
|
-
},
|
|
564
|
-
];
|
|
565
|
-
while (stack.length > 0) {
|
|
566
|
-
budget.spend('helper_state_projection');
|
|
567
|
-
const frame = stack.pop();
|
|
568
|
-
for (const access of accesses.get(frame.helperId) ?? []) {
|
|
569
|
-
const projectionKey = `${start.id}\0${access.id}`;
|
|
570
|
-
if (projectedAccesses.has(projectionKey))
|
|
571
|
-
continue;
|
|
572
|
-
projectedAccesses.add(projectionKey);
|
|
573
|
-
if (projected.length >= EVENT_GRAPH_MAX_HELPER_STATE_PROJECTIONS ||
|
|
574
|
-
graph.stateAccesses.length + projected.length >= EVENT_GRAPH_MAX_STATE_ACCESSES) {
|
|
575
|
-
addIssue(graph.issues, {
|
|
576
|
-
code: 'EVENT_HELPER_STATE_PROJECTION_LIMIT',
|
|
577
|
-
classification: 'unresolved_analysis',
|
|
578
|
-
severity: 'blocker',
|
|
579
|
-
message: 'Helper state-flow projection reached its fixed materialization ceiling; structural helper evidence remains available.',
|
|
580
|
-
confidence: 'unresolved',
|
|
581
|
-
location: start.location,
|
|
582
|
-
blockers: [
|
|
583
|
-
{
|
|
584
|
-
code: 'HELPER_STATE_PROJECTION_LIMIT',
|
|
585
|
-
message: 'Use an expanded bounded helper trace for state evidence omitted from the collapsed workspace graph.',
|
|
586
|
-
location: start.location,
|
|
587
|
-
details: { maximum: EVENT_GRAPH_MAX_HELPER_STATE_PROJECTIONS },
|
|
588
|
-
},
|
|
589
|
-
],
|
|
590
|
-
subjectIds: [start.from, start.to],
|
|
591
|
-
});
|
|
592
|
-
return projected;
|
|
593
|
-
}
|
|
594
|
-
projected.push({
|
|
595
|
-
...access,
|
|
596
|
-
id: `state:helper-projection:${hashCanonical({
|
|
597
|
-
caller: start.from,
|
|
598
|
-
access: access.id,
|
|
599
|
-
edges: frame.callEdges.map(({ id }) => id),
|
|
600
|
-
}).slice(0, 32)}`,
|
|
601
|
-
ownerId: start.from,
|
|
602
|
-
scope: access.scope === 'unknown' ? callerScope : access.scope,
|
|
603
|
-
confidence: worstConfidence(access.confidence, ...frame.callEdges.map(({ confidence }) => confidence)),
|
|
604
|
-
helperStack: frame.helperStack,
|
|
605
|
-
conditions: [
|
|
606
|
-
...frame.callEdges.flatMap(({ conditions }) => conditions),
|
|
607
|
-
...access.conditions,
|
|
608
|
-
],
|
|
609
|
-
metadata: {
|
|
610
|
-
...access.metadata,
|
|
611
|
-
projectedFromAccessId: access.id,
|
|
612
|
-
structuralEdgeIds: frame.callEdges.map(({ id }) => id),
|
|
613
|
-
},
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
if (frame.helperStack.length >= EVENT_GRAPH_MAX_HELPER_DEPTH)
|
|
497
|
+
budget.spend('helper_state_projection');
|
|
498
|
+
const calls = walk.path(record.path).map(({ id }) => edges.get(id));
|
|
499
|
+
const start = calls[0];
|
|
500
|
+
const target = calls.at(-1).to;
|
|
501
|
+
let callerScope = callerScopes.get(start.id);
|
|
502
|
+
if (callerScope === undefined) {
|
|
503
|
+
callerScope = helperCallerScope(nodes, parents, start);
|
|
504
|
+
callerScopes.set(start.id, callerScope);
|
|
505
|
+
}
|
|
506
|
+
for (const access of accesses.get(target) ?? []) {
|
|
507
|
+
const key = `${start.id}\0${access.id}`;
|
|
508
|
+
if (projectedKeys.has(key))
|
|
617
509
|
continue;
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
510
|
+
projectedKeys.add(key);
|
|
511
|
+
if (projected.length >= EVENT_GRAPH_MAX_HELPER_STATE_PROJECTIONS ||
|
|
512
|
+
graph.stateAccesses.length + projected.length >= EVENT_GRAPH_MAX_STATE_ACCESSES) {
|
|
513
|
+
addIssue(graph.issues, {
|
|
514
|
+
code: 'EVENT_HELPER_STATE_PROJECTION_LIMIT',
|
|
515
|
+
classification: 'unresolved_analysis',
|
|
516
|
+
severity: 'blocker',
|
|
517
|
+
message: 'Helper state-flow projection reached its fixed materialization ceiling; structural helper evidence remains available.',
|
|
518
|
+
confidence: 'unresolved',
|
|
519
|
+
location: start.location,
|
|
520
|
+
blockers: [
|
|
521
|
+
{
|
|
522
|
+
code: 'HELPER_STATE_PROJECTION_LIMIT',
|
|
523
|
+
message: 'Use an expanded bounded helper trace for state evidence omitted from the collapsed workspace graph.',
|
|
524
|
+
location: start.location,
|
|
525
|
+
details: { maximum: EVENT_GRAPH_MAX_HELPER_STATE_PROJECTIONS },
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
subjectIds: [start.from, start.to],
|
|
630
529
|
});
|
|
530
|
+
return projected;
|
|
631
531
|
}
|
|
532
|
+
projected.push({
|
|
533
|
+
...access,
|
|
534
|
+
id: `state:helper-projection:${hashCanonical({ caller: start.from, access: access.id, edges: calls.map(({ id }) => id) }).slice(0, 32)}`,
|
|
535
|
+
ownerId: start.from,
|
|
536
|
+
scope: access.scope === 'unknown' ? callerScope : access.scope,
|
|
537
|
+
confidence: worstConfidence(access.confidence, ...calls.map(({ confidence }) => confidence)),
|
|
538
|
+
helperStack: calls.map(({ to }) => helperName(nodes.get(to))),
|
|
539
|
+
conditions: [...calls.flatMap(({ conditions }) => conditions), ...access.conditions],
|
|
540
|
+
metadata: {
|
|
541
|
+
...access.metadata,
|
|
542
|
+
projectedFromAccessId: access.id,
|
|
543
|
+
structuralEdgeIds: calls.map(({ id }) => id),
|
|
544
|
+
},
|
|
545
|
+
});
|
|
632
546
|
}
|
|
633
547
|
}
|
|
634
548
|
return stableUnique(projected);
|
|
@@ -1450,6 +1364,22 @@ function unreachableIssues(graph, budget) {
|
|
|
1450
1364
|
}
|
|
1451
1365
|
function analyzeFragments(snapshot, files, catalog, options, budget) {
|
|
1452
1366
|
const fragments = [];
|
|
1367
|
+
const dependencies = beginSemanticAnalysis(options.cache, snapshot, 'event', options.workspaceIdentity);
|
|
1368
|
+
const reads = new SemanticCatalog();
|
|
1369
|
+
const context = {
|
|
1370
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
1371
|
+
budget,
|
|
1372
|
+
knownEventIds: reads.bind('events', 'event', catalog.eventIds),
|
|
1373
|
+
knownEventTypes: reads.bind('event-types', 'event', catalog.eventTypes),
|
|
1374
|
+
knownHelperIds: reads.bind('helpers', 'scripted_effect', catalog.helperIds),
|
|
1375
|
+
knownDecisionIds: reads.bind('decisions', 'decision', catalog.decisionIds),
|
|
1376
|
+
activeEventPaths: reads.bind('active-events', 'event', catalog.activeEventPaths),
|
|
1377
|
+
retainedEventPaths: reads.bind('retained-events', 'event', catalog.retainedEventPaths),
|
|
1378
|
+
activeHelperPaths: reads.bind('active-helpers', 'scripted_effect', catalog.activeHelperPaths),
|
|
1379
|
+
activeDecisionPaths: reads.bind('active-decisions', 'decision', catalog.activeDecisionPaths),
|
|
1380
|
+
inventoryComplete: snapshot.complete,
|
|
1381
|
+
catalogFingerprint: catalog.fingerprint,
|
|
1382
|
+
};
|
|
1453
1383
|
for (const file of files) {
|
|
1454
1384
|
budget.spend('source_fragment');
|
|
1455
1385
|
if (!shouldAnalyzeEventFile(file))
|
|
@@ -1457,24 +1387,7 @@ function analyzeFragments(snapshot, files, catalog, options, budget) {
|
|
|
1457
1387
|
if (snapshot.index.isSourceSkipped(file.displayPath))
|
|
1458
1388
|
continue;
|
|
1459
1389
|
const cacheKey = eventSemanticFragmentCacheKey(file, catalog.fingerprint);
|
|
1460
|
-
|
|
1461
|
-
if (fragment === undefined) {
|
|
1462
|
-
fragment = analyzeEventSource(file, {
|
|
1463
|
-
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
1464
|
-
budget,
|
|
1465
|
-
knownEventIds: catalog.eventIds,
|
|
1466
|
-
knownEventTypes: catalog.eventTypes,
|
|
1467
|
-
knownHelperIds: catalog.helperIds,
|
|
1468
|
-
knownDecisionIds: catalog.decisionIds,
|
|
1469
|
-
activeEventPaths: catalog.activeEventPaths,
|
|
1470
|
-
retainedEventPaths: catalog.retainedEventPaths,
|
|
1471
|
-
activeHelperPaths: catalog.activeHelperPaths,
|
|
1472
|
-
activeDecisionPaths: catalog.activeDecisionPaths,
|
|
1473
|
-
inventoryComplete: snapshot.complete,
|
|
1474
|
-
catalogFingerprint: catalog.fingerprint,
|
|
1475
|
-
});
|
|
1476
|
-
options.cache?.set(cacheKey, fragment, file.bytes.length);
|
|
1477
|
-
}
|
|
1390
|
+
const fragment = semanticFragment(dependencies, options.cache, file, cacheKey, reads, () => analyzeEventSource(file, context));
|
|
1478
1391
|
fragments.push(fragment);
|
|
1479
1392
|
}
|
|
1480
1393
|
return fragments.sort((left, right) => compareCodeUnits(left.sourcePath, right.sourcePath) ||
|
|
@@ -1543,6 +1456,12 @@ function graphStatistics(nodes, edges, stateAccesses, issues) {
|
|
|
1543
1456
|
* Parsing remains file-local; this stage owns cross-file resolution and static projections.
|
|
1544
1457
|
*/
|
|
1545
1458
|
export function buildEventGraph(snapshot, options = {}) {
|
|
1459
|
+
return runDependencyAnalysis(eventGraphAnalysis(snapshot, options), options.signal);
|
|
1460
|
+
}
|
|
1461
|
+
export function buildEventGraphAsync(snapshot, options = {}) {
|
|
1462
|
+
return runDependencyAnalysisAsync(eventGraphAnalysis(snapshot, options), options.signal);
|
|
1463
|
+
}
|
|
1464
|
+
function* eventGraphAnalysis(snapshot, options) {
|
|
1546
1465
|
options.signal?.throwIfAborted();
|
|
1547
1466
|
const budget = new EventAnalysisBudget(options.signal);
|
|
1548
1467
|
const files = activeFiles(snapshot);
|
|
@@ -1560,9 +1479,10 @@ export function buildEventGraph(snapshot, options = {}) {
|
|
|
1560
1479
|
normalizeCrossFileEdges(graph);
|
|
1561
1480
|
ensureReferencedNodes(graph);
|
|
1562
1481
|
if (options.projectHelpers !== false) {
|
|
1563
|
-
|
|
1482
|
+
const walk = yield helperWalkInput(graph, snapshot.revision);
|
|
1483
|
+
for (const edge of helperProjections(graph, budget, walk))
|
|
1564
1484
|
graph.edges.push(edge);
|
|
1565
|
-
for (const access of projectHelperStateAccesses(graph, budget))
|
|
1485
|
+
for (const access of projectHelperStateAccesses(graph, budget, walk))
|
|
1566
1486
|
graph.stateAccesses.push(access);
|
|
1567
1487
|
}
|
|
1568
1488
|
addTerminalNodes(graph);
|
|
@@ -1606,7 +1526,11 @@ export function buildEventGraph(snapshot, options = {}) {
|
|
|
1606
1526
|
const diagnostics = sortDiagnostics([...snapshot.diagnostics, ...partial.diagnostics]);
|
|
1607
1527
|
// Coverage completeness is independent from defects discovered in the source. A full,
|
|
1608
1528
|
// untruncated analysis remains complete when it correctly reports missing or dynamic links.
|
|
1609
|
-
const complete = !focused &&
|
|
1529
|
+
const complete = !focused &&
|
|
1530
|
+
snapshot.complete &&
|
|
1531
|
+
!graph.issues.some(({ code }) => code === 'EVENT_HELPER_DEPTH_LIMIT' ||
|
|
1532
|
+
code === 'EVENT_HELPER_PROJECTION_LIMIT' ||
|
|
1533
|
+
code === 'EVENT_HELPER_STATE_PROJECTION_LIMIT');
|
|
1610
1534
|
const nodes = sortNodes(graph.nodes);
|
|
1611
1535
|
const edges = stableUnique(graph.edges);
|
|
1612
1536
|
const stateAccesses = stableUnique(graph.stateAccesses);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { compareCodeUnits } from '../core/canonical.js';
|
|
2
|
+
export function eventHelperInventory(graph) {
|
|
3
|
+
const helpers = new Set(graph.nodes.filter(({ kind }) => kind === 'helper').map(({ id }) => id));
|
|
4
|
+
const structural = graph.edges.filter(({ derived }) => !derived);
|
|
5
|
+
return {
|
|
6
|
+
domain: 'event',
|
|
7
|
+
sourceRevision: graph.revision,
|
|
8
|
+
sourceComplete: graph.complete,
|
|
9
|
+
sourceHashes: graph.sourceHashes,
|
|
10
|
+
unresolved: graph.unresolved,
|
|
11
|
+
edges: [
|
|
12
|
+
...structural.map((edge) => ({
|
|
13
|
+
id: edge.id,
|
|
14
|
+
from: edge.from,
|
|
15
|
+
to: edge.to,
|
|
16
|
+
evidence: { kind: 'event_edge', edge },
|
|
17
|
+
})),
|
|
18
|
+
...graph.stateAccesses
|
|
19
|
+
.filter(({ ownerId }) => helpers.has(ownerId))
|
|
20
|
+
.map((access) => ({
|
|
21
|
+
id: `state-access:${access.id}`,
|
|
22
|
+
from: access.ownerId,
|
|
23
|
+
to: `state-access:${access.id}`,
|
|
24
|
+
evidence: { kind: 'state_access', access },
|
|
25
|
+
})),
|
|
26
|
+
].sort((left, right) => compareCodeUnits(left.id, right.id)),
|
|
27
|
+
roots: structural
|
|
28
|
+
.filter(({ from, to }) => !helpers.has(from) && helpers.has(to))
|
|
29
|
+
.map(({ id }) => id)
|
|
30
|
+
.sort(compareCodeUnits),
|
|
31
|
+
branches: [...helpers].sort(compareCodeUnits),
|
|
32
|
+
};
|
|
33
|
+
}
|