progmune-runtime 2.1.5 → 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/README.md +108 -468
- package/dist/ablation-study.js +144 -0
- package/dist/ablation-study.test.js +18 -0
- package/dist/action-runtime.js +3 -1
- package/dist/active-learning.js +211 -0
- package/dist/analytics.js +139 -0
- package/dist/asset-factory.js +309 -0
- package/dist/asset-growth.js +244 -0
- package/dist/asset-promotion.js +382 -0
- package/dist/asset-quality.js +550 -0
- package/dist/audit/business-translator.js +285 -0
- package/dist/audit/cli.js +66 -0
- package/dist/audit/formatters/html.js +379 -0
- package/dist/audit/formatters/json.js +11 -0
- package/dist/audit/formatters/markdown.js +192 -0
- package/dist/audit/formatters/terminal.js +189 -0
- package/dist/audit/index.js +25 -0
- package/dist/audit/report-builder.js +318 -0
- package/dist/audit/types.js +8 -0
- package/dist/audit.js +3 -3
- package/dist/auto-benchmark-generator.js +137 -0
- package/dist/auto-benchmark-generator.test.js +45 -0
- package/dist/auto-protocol-synthesizer.js +362 -0
- package/dist/auto-protocol-synthesizer.test.js +82 -0
- package/dist/autonomous-patch.js +175 -0
- package/dist/autonomous-patch.test.js +128 -0
- package/dist/badge/badge-server.js +98 -0
- package/dist/behavior-miner.js +442 -0
- package/dist/belief-layer.js +475 -0
- package/dist/benchmark-count.js +5 -0
- package/dist/benchmark-generator.js +211 -0
- package/dist/benchmark-harness.js +201 -0
- package/dist/benchmark-pass-rate.js +7 -0
- package/dist/benchmark-report.js +8 -3
- package/dist/benchmark-save.js +14 -1
- package/dist/bootstrap-validation.js +197 -0
- package/dist/bootstrap-validation.test.js +51 -0
- package/dist/branch-ledger.js +1 -1
- package/dist/capability-gap.js +130 -0
- package/dist/certify-html.js +351 -0
- package/dist/certify.js +326 -0
- package/dist/check.js +4 -4
- package/dist/compliance-miner.js +447 -0
- package/dist/continuous-benchmark.js +194 -0
- package/dist/continuous-benchmark.test.js +116 -0
- package/dist/corpus-stats.js +173 -0
- package/dist/counterfactual-engine.js +288 -0
- package/dist/coverage-dashboard.js +109 -0
- package/dist/coverage-system.test.js +205 -0
- package/dist/cross-repo-precision.js +352 -0
- package/dist/cve-benchmark.js +180 -0
- package/dist/cve-benchmark.test.js +28 -0
- package/dist/cve-collector.js +73 -0
- package/dist/data-quality.js +141 -0
- package/dist/decision-engine.js +388 -0
- package/dist/derive-metadata.js +250 -0
- package/dist/difficulty-active.test.js +198 -0
- package/dist/difficulty-map.js +244 -0
- package/dist/discovery-analytics.js +125 -0
- package/dist/discovery-model.js +149 -0
- package/dist/discovery-optimize.test.js +199 -0
- package/dist/discovery-trace.js +276 -0
- package/dist/discovery-trace.test.js +97 -0
- package/dist/emitter.js +83 -1
- package/dist/enterprise-dashboard.js +405 -0
- package/dist/eval-hardening.js +297 -0
- package/dist/eval-hardening.test.js +85 -0
- package/dist/evaluation-campaign.js +359 -0
- package/dist/evaluation-campaign.test.js +181 -0
- package/dist/evidence-growth.js +143 -0
- package/dist/evidence-repository.js +209 -0
- package/dist/evidence-system.js +441 -0
- package/dist/execute.js +15 -7
- package/dist/experimental/software-physics.js +291 -0
- package/dist/experimental/state-inference.js +516 -0
- package/dist/experimental/unsupervised-physics.js +230 -0
- package/dist/extract-ir-python.js +54 -7
- package/dist/extract-ir.js +376 -12
- package/dist/failure-collector.js +2 -2
- package/dist/failure-corpus.js +322 -9
- package/dist/feedback.js +16 -5
- package/dist/feedback.test.js +49 -0
- package/dist/file-lock.js +1 -1
- package/dist/flywheel-batch.js +292 -0
- package/dist/frameworks/express-cli.js +237 -0
- package/dist/frameworks/express-detector.js +445 -0
- package/dist/frameworks/express-detector.test.js +206 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/nestjs-detector.js +302 -0
- package/dist/frameworks/trpc-detector.js +161 -0
- package/dist/frameworks/version-awareness.js +179 -0
- package/dist/function-synonyms.js +164 -0
- package/dist/function-synonyms.test.js +68 -0
- package/dist/generalization.test.js +352 -0
- package/dist/goal-annotator.js +113 -0
- package/dist/goal-planner.js +563 -0
- package/dist/gold-cve.js +164 -0
- package/dist/gold-cve.test.js +104 -0
- package/dist/gold-quality.js +206 -0
- package/dist/gold-tiers.js +241 -0
- package/dist/governance-dashboard.js +327 -0
- package/dist/graph-viz.js +240 -0
- package/dist/guided-frontier.js +195 -0
- package/dist/hierarchical-planner.js +148 -0
- package/dist/identifier-parser.js +260 -0
- package/dist/immune-metrics.js +93 -0
- package/dist/immune-receiver.js +158 -0
- package/dist/immune-reporter.js +1 -1
- package/dist/improvement-orchestrator.js +206 -0
- package/dist/inject-p0-vocabulary.js +300 -0
- package/dist/intent-parser.js +218 -0
- package/dist/invariant-algebra.js +476 -0
- package/dist/invariant-calculus.js +533 -0
- package/dist/ir-utils.js +70 -0
- package/dist/ir-utils.test.js +50 -0
- package/dist/knowledge-api.js +312 -0
- package/dist/knowledge-evolution.js +452 -0
- package/dist/knowledge-explorer.js +506 -0
- package/dist/knowledge-flywheel.js +274 -0
- package/dist/knowledge-governance.js +338 -0
- package/dist/knowledge-governance.test.js +150 -0
- package/dist/knowledge-graph.js +181 -0
- package/dist/knowledge-guided-synth.js +246 -0
- package/dist/knowledge-loop.test.js +77 -0
- package/dist/knowledge-object.js +316 -0
- package/dist/knowledge-package.js +98 -0
- package/dist/kpi-dashboard.js +561 -0
- package/dist/l3-cross-function.js +280 -0
- package/dist/learning-ranker.js +148 -0
- package/dist/learning-ranker.test.js +291 -0
- package/dist/ledger/accountability.js +322 -0
- package/dist/ledger/chain-builder.js +185 -0
- package/dist/ledger/cli.js +222 -0
- package/dist/ledger/index.js +13 -0
- package/dist/ledger/signatures.js +193 -0
- package/dist/ledger/types.js +9 -0
- package/dist/llm.js +74 -3
- package/dist/load-benchmarks.js +8 -3
- package/dist/logger.js +66 -0
- package/dist/logger.test.js +37 -0
- package/dist/logistic-reward.js +339 -0
- package/dist/logistic-reward.test.js +180 -0
- package/dist/macro-graph.js +193 -0
- package/dist/macro-repair.js +183 -0
- package/dist/mcp-server.mjs +1202 -483
- package/dist/memory-layer.js +42 -5
- package/dist/multi-repo-precision.js +422 -0
- package/dist/name-free-protocol.js +425 -0
- package/dist/name-free-protocol.test.js +170 -0
- package/dist/name-scrambling.js +138 -0
- package/dist/name-scrambling.test.js +16 -0
- package/dist/p3-observability.test.js +281 -0
- package/dist/p5-orchestrator.test.js +225 -0
- package/dist/pairwise-preference.js +294 -0
- package/dist/pairwise-preference.test.js +140 -0
- package/dist/planner-constraints.js +104 -0
- package/dist/planner-prompts.js +155 -0
- package/dist/planner-telemetry.js +415 -0
- package/dist/planner-trace.js +214 -0
- package/dist/planner.js +162 -167
- package/dist/plsb/artifact.js +116 -0
- package/dist/plsb/cli.js +71 -0
- package/dist/plsb/index.js +19 -0
- package/dist/plsb/leaderboard.js +249 -0
- package/dist/plsb/report-md.js +156 -0
- package/dist/plsb/schema.js +179 -0
- package/dist/plsb-benchmark.js +284 -0
- package/dist/plsb-benchmark.test.js +119 -0
- package/dist/policy/cli.js +134 -0
- package/dist/policy/engine.js +333 -0
- package/dist/policy/index.js +12 -0
- package/dist/policy/types.js +59 -0
- package/dist/policy-miner.js +505 -0
- package/dist/precision-analyze.js +229 -0
- package/dist/precision-benchmark.js +147 -0
- package/dist/precision-label-c.js +134 -0
- package/dist/precision-label.js +193 -0
- package/dist/precision-report-c.js +149 -0
- package/dist/precision-report.js +246 -0
- package/dist/progmune-status.js +108 -0
- package/dist/proof-engine.js +479 -0
- package/dist/proof-provenance.js +315 -0
- package/dist/protocol-coverage.js +294 -0
- package/dist/protocol-detector.js +1189 -0
- package/dist/protocol-embedding-expanded.js +297 -0
- package/dist/protocol-embedding-expanded.test.js +97 -0
- package/dist/protocol-embedding.js +195 -0
- package/dist/protocol-embedding.test.js +82 -0
- package/dist/protocol-extractor-v2.js +354 -0
- package/dist/protocol-extractor-v2.test.js +140 -0
- package/dist/protocol-extractor.js +310 -0
- package/dist/protocol-extractor.test.js +113 -0
- package/dist/protocol-foundation.js +322 -0
- package/dist/protocol-foundation.test.js +163 -0
- package/dist/protocol-frontier.js +243 -0
- package/dist/protocol-frontier.test.js +92 -0
- package/dist/protocol-gap-analyzer.js +228 -0
- package/dist/protocol-gap-analyzer.test.js +49 -0
- package/dist/protocol-invariants.js +276 -0
- package/dist/protocol-invariants.test.js +111 -0
- package/dist/protocol-knowledge.js +464 -0
- package/dist/protocol-miner.js +343 -0
- package/dist/protocol-mining.js +207 -0
- package/dist/protocol-mining.test.js +37 -0
- package/dist/protocol-registry.js +1 -1
- package/dist/protocol-security-benchmark.js +222 -0
- package/dist/protocol-vulnerability.js +257 -0
- package/dist/protocol-vulnerability.test.js +60 -0
- package/dist/python-benchmark.js +120 -0
- package/dist/python-emitter.js +163 -45
- package/dist/python-protocol-extractor.js +187 -0
- package/dist/python-protocol-extractor.test.js +116 -0
- package/dist/realworld-benchmark.js +646 -0
- package/dist/realworld-benchmark.test.js +36 -0
- package/dist/repair-arch.test.js +411 -0
- package/dist/repair-evolution.test.js +454 -0
- package/dist/repair-executor.js +719 -0
- package/dist/repair-proposal.js +4 -4
- package/dist/repair-ranker.js +141 -0
- package/dist/repair-strategies.js +419 -0
- package/dist/repair-taxonomy.js +234 -0
- package/dist/repair-types.js +12 -0
- package/dist/repo-evaluator.js +250 -0
- package/dist/repo-evaluator.test.js +128 -0
- package/dist/resource-abstraction.js +242 -0
- package/dist/resource-detector.js +211 -0
- package/dist/result.test.js +43 -0
- package/dist/reward-system.js +411 -0
- package/dist/reward-system.test.js +175 -0
- package/dist/risk-model.js +215 -0
- package/dist/rule-miner.js +234 -7
- package/dist/rule-specificity.js +254 -0
- package/dist/runtime-types.js +27 -0
- package/dist/scaffold.js +208 -0
- package/dist/scale-collector.test.js +101 -0
- package/dist/scale-trajectory-collector.js +128 -0
- package/dist/sdk.js +250 -0
- package/dist/search-planner.js +4 -41
- package/dist/semantic-snapshot.js +1 -1
- package/dist/semantic-topology.js +121 -0
- package/dist/semantic-trace.js +310 -317
- package/dist/sequence-extractor.js +343 -0
- package/dist/skill-library.js +245 -0
- package/dist/skill-planner.test.js +189 -0
- package/dist/software-physics.js +291 -0
- package/dist/software-physics.test.js +81 -0
- package/dist/ssg-precision.js +478 -0
- package/dist/ssg-validator.js +71 -21
- package/dist/state-inference-doubleblind.test.js +160 -0
- package/dist/state-inference.js +516 -0
- package/dist/state-inference.test.js +115 -0
- package/dist/state-machine-fingerprint.js +345 -0
- package/dist/state-machine-fingerprint.test.js +120 -0
- package/dist/state-miner.js +386 -0
- package/dist/state-name-inference.js +213 -0
- package/dist/state-name-inference.test.js +69 -0
- package/dist/strategy-planner.js +326 -59
- package/dist/strategy-planner.test.js +135 -0
- package/dist/telemetry-analytics.test.js +402 -0
- package/dist/terminal-format.js +68 -0
- package/dist/terminal-format.test.js +83 -0
- package/dist/topology-factory.js +196 -0
- package/dist/topology-representation.js +242 -0
- package/dist/topology-representation.test.js +27 -0
- package/dist/trajectory-augmentation.js +254 -0
- package/dist/trajectory-augmentation.test.js +63 -0
- package/dist/trajectory-corpus.js +440 -0
- package/dist/trajectory-corpus.test.js +32 -0
- package/dist/trajectory-feedback.test.js +116 -0
- package/dist/transition-synthesizer.js +286 -0
- package/dist/transition-synthesizer.test.js +123 -0
- package/dist/trust/api-semantic-mapper.js +809 -0
- package/dist/trust/call-graph-propagator.js +225 -0
- package/dist/trust/cli.js +122 -0
- package/dist/trust/compliance-scorer.js +283 -0
- package/dist/trust/confidence-calculator.js +261 -0
- package/dist/trust/engine.js +1145 -0
- package/dist/trust/explainability.js +85 -0
- package/dist/trust/formatters/ci.js +42 -0
- package/dist/trust/formatters/json.js +11 -0
- package/dist/trust/formatters/terminal.js +152 -0
- package/dist/trust/index.js +39 -0
- package/dist/trust/phase1-verify.js +171 -0
- package/dist/trust/protocol-domain-validator.js +697 -0
- package/dist/trust/score-calculator.js +282 -0
- package/dist/trust/ssg-bridge.js +641 -0
- package/dist/trust/ssg-bridge.test.js +269 -0
- package/dist/trust/types.js +67 -0
- package/dist/trust/violation-trace.js +335 -0
- package/dist/trust-api.js +179 -0
- package/dist/trust-calibration.js +279 -0
- package/dist/unknown-protocol-discovery.js +339 -0
- package/dist/unknown-protocol-discovery.test.js +102 -0
- package/dist/unsupervised-physics.js +230 -0
- package/dist/unsupervised-physics.test.js +95 -0
- package/dist/utils.test.js +37 -0
- package/dist/validator.js +187 -10
- package/dist/verification-intelligence.js +475 -0
- package/dist/verify-api.js +432 -0
- package/dist/vi-impact-report.js +293 -0
- package/dist/wl-fingerprint.js +162 -0
- package/dist/wl-fingerprint.test.js +130 -0
- package/dist/zeroshot-strategy.js +139 -0
- package/docs/Progmune_/346/212/225/350/265/204/344/272/272/347/231/275/347/232/256/344/271/246_v2.0.html +576 -0
- package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +710 -0
- package/package.json +74 -7
- package/protocols.json +1956 -50
- package/.dockerignore +0 -14
- package/.mcp.json +0 -11
- package/.progmune_allowlist +0 -50
- package/.test_report/test_report.md +0 -87
- package/Dockerfile +0 -9
- package/FAQ.md +0 -167
- package/WHITEPAPER.md +0 -540
- package/demo-project/auth.ts +0 -55
- package/demo-project/tsconfig.json +0 -8
- package/dist/acl-breakdown.js +0 -13
- package/dist/all-sessions.js +0 -11
- package/dist/antibody-stats.js +0 -11
- package/dist/branch-tree-count.js +0 -14
- package/dist/common-fixpath.js +0 -12
- package/dist/constraint-types.js +0 -12
- package/dist/exec-metrics.js +0 -11
- package/dist/failure-report.js +0 -11
- package/dist/fast-path-hits.js +0 -13
- package/dist/fingerprint-list.js +0 -15
- package/dist/gen-history-log.js +0 -13
- package/dist/heatmap-data.js +0 -11
- package/dist/recent-session.js +0 -12
- package/dist/svl-distribution.js +0 -11
- package/dist/terminal-status.js +0 -11
- package/dist/token-savings.js +0 -11
- package/dist/total-repairs.js +0 -12
- package/dist/unresolved-count.js +0 -12
- package/dist/valid-fingerprints.js +0 -13
- package/dist/verify-ledgers.js +0 -11
- package/docs/whitepaper-style.css +0 -77
- package/docs/whitepaper-v2.1.md +0 -609
- package/docs/whitepaper-v2.2.md +0 -1064
- package/docs/whitepaper-v2.2.pdf +0 -0
- package/fly.toml +0 -31
- package/public/dashboard.html +0 -119
- package/server/hub.js +0 -116
- package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
- package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
- package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
- package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
- package/test/replay-golden.ts +0 -84
- package/test_benchmark.js +0 -165
- package/test_comprehensive.mjs +0 -638
- package/test_concurrency.js +0 -129
- package/test_ir_robustness.js +0 -85
- package/test_semantic_contracts.js +0 -269
- package/test_ssg_stress.js +0 -156
- package/test_svl3.js +0 -58
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P8.1: State Inference Engine — From raw call sequences to state machines
|
|
4
|
+
*
|
|
5
|
+
* Core insight: a protocol STATE is defined by what calls produce it,
|
|
6
|
+
* consume it, and invalidate it — NOT by function names.
|
|
7
|
+
*
|
|
8
|
+
* This engine takes raw call sequences (strings) and infers state machines
|
|
9
|
+
* using purely structural co-occurrence analysis. Zero dependency on
|
|
10
|
+
* function names, keywords, or pre-written protocol rules.
|
|
11
|
+
*
|
|
12
|
+
* Pipeline:
|
|
13
|
+
* Raw call sequences
|
|
14
|
+
* → Assign structural roles (producer/consumer/invalidator)
|
|
15
|
+
* → Group functions into states by structural equivalence
|
|
16
|
+
* → Build state transition graph (opaque S0, S1, S2...)
|
|
17
|
+
* → Extract state machine fingerprint
|
|
18
|
+
*
|
|
19
|
+
* The decisive test: after scrambling all function names to F_001, F_002,
|
|
20
|
+
* the inferred state machine must be identical to the original.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.inferStateMachine = inferStateMachine;
|
|
24
|
+
exports.extractStateFingerprint = extractStateFingerprint;
|
|
25
|
+
exports.stateFingerprintToVector = stateFingerprintToVector;
|
|
26
|
+
exports.stateFingerprintSimilarity = stateFingerprintSimilarity;
|
|
27
|
+
exports.printInferredStateMachine = printInferredStateMachine;
|
|
28
|
+
function assignRoles(fnIndices, sequences) {
|
|
29
|
+
const N = fnIndices.size;
|
|
30
|
+
const roles = Array.from({ length: N }, (_, i) => ({
|
|
31
|
+
index: i,
|
|
32
|
+
asFirst: 0,
|
|
33
|
+
asLast: 0,
|
|
34
|
+
asMiddle: 0,
|
|
35
|
+
totalOccurrences: 0,
|
|
36
|
+
predecessors: new Set(),
|
|
37
|
+
successors: new Set(),
|
|
38
|
+
role: "isolated",
|
|
39
|
+
}));
|
|
40
|
+
for (const seq of sequences) {
|
|
41
|
+
if (seq.length === 0)
|
|
42
|
+
continue;
|
|
43
|
+
for (let pos = 0; pos < seq.length; pos++) {
|
|
44
|
+
const idx = fnIndices.get(seq[pos]);
|
|
45
|
+
if (idx === undefined)
|
|
46
|
+
continue;
|
|
47
|
+
roles[idx].totalOccurrences++;
|
|
48
|
+
if (pos === 0)
|
|
49
|
+
roles[idx].asFirst++;
|
|
50
|
+
if (pos === seq.length - 1)
|
|
51
|
+
roles[idx].asLast++;
|
|
52
|
+
if (pos > 0 && pos < seq.length - 1)
|
|
53
|
+
roles[idx].asMiddle++;
|
|
54
|
+
if (pos > 0) {
|
|
55
|
+
const prevIdx = fnIndices.get(seq[pos - 1]);
|
|
56
|
+
if (prevIdx !== undefined)
|
|
57
|
+
roles[idx].predecessors.add(prevIdx);
|
|
58
|
+
}
|
|
59
|
+
if (pos < seq.length - 1) {
|
|
60
|
+
const nextIdx = fnIndices.get(seq[pos + 1]);
|
|
61
|
+
if (nextIdx !== undefined)
|
|
62
|
+
roles[idx].successors.add(nextIdx);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Classify: entry (mostly first), exit (mostly last), bridge (both), isolated (neither)
|
|
67
|
+
for (const r of roles) {
|
|
68
|
+
const total = Math.max(1, r.totalOccurrences);
|
|
69
|
+
const firstRate = r.asFirst / total;
|
|
70
|
+
const lastRate = r.asLast / total;
|
|
71
|
+
if (firstRate > 0.5 && r.successors.size > 0)
|
|
72
|
+
r.role = "entry";
|
|
73
|
+
else if (lastRate > 0.5 && r.predecessors.size > 0)
|
|
74
|
+
r.role = "exit";
|
|
75
|
+
else if (r.predecessors.size > 0 || r.successors.size > 0)
|
|
76
|
+
r.role = "bridge";
|
|
77
|
+
else
|
|
78
|
+
r.role = "isolated";
|
|
79
|
+
}
|
|
80
|
+
return roles;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Group functions that share the same role and similar neighbor patterns
|
|
84
|
+
* into opaque states (S0, S1, S2...).
|
|
85
|
+
*/
|
|
86
|
+
function groupIntoStates(roles) {
|
|
87
|
+
const N = roles.length;
|
|
88
|
+
// Strategy: functions with the same role AND overlapping predecessor/successor
|
|
89
|
+
// sets likely belong to the same protocol state.
|
|
90
|
+
const groups = [];
|
|
91
|
+
const assigned = new Set();
|
|
92
|
+
// Pass 1: Group by role first
|
|
93
|
+
for (const roleType of ["entry", "bridge", "exit", "isolated"]) {
|
|
94
|
+
const candidates = roles
|
|
95
|
+
.map((r, i) => ({ ...r, index: i }))
|
|
96
|
+
.filter(r => r.role === roleType && !assigned.has(r.index));
|
|
97
|
+
// Within the same role, group by neighbor overlap
|
|
98
|
+
const grouped = [];
|
|
99
|
+
const used = new Set();
|
|
100
|
+
for (const c of candidates) {
|
|
101
|
+
if (used.has(c.index))
|
|
102
|
+
continue;
|
|
103
|
+
const cluster = [c.index];
|
|
104
|
+
used.add(c.index);
|
|
105
|
+
// Find other candidates that share ≥50% neighbor overlap
|
|
106
|
+
for (const other of candidates) {
|
|
107
|
+
if (used.has(other.index))
|
|
108
|
+
continue;
|
|
109
|
+
const predOverlap = intersectSize(c.predecessors, other.predecessors);
|
|
110
|
+
const succOverlap = intersectSize(c.successors, other.successors);
|
|
111
|
+
const total = Math.max(1, Math.max(c.predecessors.size, other.predecessors.size) +
|
|
112
|
+
Math.max(c.successors.size, other.successors.size));
|
|
113
|
+
if ((predOverlap + succOverlap) / total > 0.3) {
|
|
114
|
+
cluster.push(other.index);
|
|
115
|
+
used.add(other.index);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
grouped.push(cluster);
|
|
119
|
+
}
|
|
120
|
+
for (const cluster of grouped) {
|
|
121
|
+
const stateId = `S${groups.length}`;
|
|
122
|
+
const predStates = new Set();
|
|
123
|
+
const succStates = new Set();
|
|
124
|
+
for (const fnIdx of cluster) {
|
|
125
|
+
assigned.add(fnIdx);
|
|
126
|
+
for (const p of roles[fnIdx].predecessors)
|
|
127
|
+
predStates.add(`fn_${p}`);
|
|
128
|
+
for (const s of roles[fnIdx].successors)
|
|
129
|
+
succStates.add(`fn_${s}`);
|
|
130
|
+
}
|
|
131
|
+
groups.push({
|
|
132
|
+
id: stateId,
|
|
133
|
+
members: cluster,
|
|
134
|
+
predecessorStates: predStates,
|
|
135
|
+
successorStates: succStates,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return groups;
|
|
140
|
+
}
|
|
141
|
+
function intersectSize(a, b) {
|
|
142
|
+
let count = 0;
|
|
143
|
+
for (const x of a)
|
|
144
|
+
if (b.has(x))
|
|
145
|
+
count++;
|
|
146
|
+
return count;
|
|
147
|
+
}
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════
|
|
149
|
+
// Step 3: Build state transition graph
|
|
150
|
+
// ═══════════════════════════════════════════════════════════════
|
|
151
|
+
function buildStateTransitions(groups, roles) {
|
|
152
|
+
const S = groups.length;
|
|
153
|
+
const matrix = Array.from({ length: S }, () => new Array(S).fill(0));
|
|
154
|
+
// For each pair of states, count how many functions in state A
|
|
155
|
+
// are directly followed by functions in state B in any sequence
|
|
156
|
+
const fnToState = new Map();
|
|
157
|
+
for (let s = 0; s < S; s++) {
|
|
158
|
+
for (const fnIdx of groups[s].members) {
|
|
159
|
+
fnToState.set(fnIdx, s);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Count transitions between states by analyzing function-to-function edges
|
|
163
|
+
for (let si = 0; si < S; si++) {
|
|
164
|
+
for (const fnIdx of groups[si].members) {
|
|
165
|
+
for (const succFn of roles[fnIdx].successors) {
|
|
166
|
+
const sj = fnToState.get(succFn);
|
|
167
|
+
if (sj !== undefined) {
|
|
168
|
+
matrix[si][sj]++;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return matrix;
|
|
174
|
+
}
|
|
175
|
+
// ═══════════════════════════════════════════════════════════════
|
|
176
|
+
// Step 4: Extract fingerprint
|
|
177
|
+
// ═══════════════════════════════════════════════════════════════
|
|
178
|
+
function computeStateGraphStats(matrix, groups) {
|
|
179
|
+
const S = matrix.length;
|
|
180
|
+
if (S === 0)
|
|
181
|
+
return { isDAG: true, diameter: 0, sccCount: 0, avgBranching: 0, density: 0 };
|
|
182
|
+
// DAG check: Kahn's algorithm
|
|
183
|
+
const inDeg = new Array(S).fill(0);
|
|
184
|
+
for (let i = 0; i < S; i++)
|
|
185
|
+
for (let j = 0; j < S; j++)
|
|
186
|
+
if (matrix[i][j] > 0)
|
|
187
|
+
inDeg[j]++;
|
|
188
|
+
const q = [];
|
|
189
|
+
for (let i = 0; i < S; i++)
|
|
190
|
+
if (inDeg[i] === 0)
|
|
191
|
+
q.push(i);
|
|
192
|
+
let visited = 0;
|
|
193
|
+
while (q.length > 0) {
|
|
194
|
+
const n = q.shift();
|
|
195
|
+
visited++;
|
|
196
|
+
for (let j = 0; j < S; j++) {
|
|
197
|
+
if (matrix[n][j] > 0 && --inDeg[j] === 0)
|
|
198
|
+
q.push(j);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const isDAG = visited === S;
|
|
202
|
+
// Diameter: BFS from each node
|
|
203
|
+
let diameter = 0;
|
|
204
|
+
for (let start = 0; start < S; start++) {
|
|
205
|
+
const dist = new Array(S).fill(-1);
|
|
206
|
+
const bq = [start];
|
|
207
|
+
dist[start] = 0;
|
|
208
|
+
while (bq.length > 0) {
|
|
209
|
+
const n = bq.shift();
|
|
210
|
+
for (let j = 0; j < S; j++) {
|
|
211
|
+
if (matrix[n][j] > 0 && dist[j] === -1) {
|
|
212
|
+
dist[j] = dist[n] + 1;
|
|
213
|
+
diameter = Math.max(diameter, dist[j]);
|
|
214
|
+
bq.push(j);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// SCC count: Kosaraju simplification (undirected component count as proxy)
|
|
220
|
+
const undirected = new Set();
|
|
221
|
+
for (let i = 0; i < S; i++)
|
|
222
|
+
for (let j = 0; j < S; j++)
|
|
223
|
+
if (matrix[i][j] > 0 || matrix[j][i] > 0)
|
|
224
|
+
undirected.add(`${Math.min(i, j)},${Math.max(i, j)}`);
|
|
225
|
+
const comps = new Map();
|
|
226
|
+
let compId = 0;
|
|
227
|
+
for (const edge of undirected) {
|
|
228
|
+
const [a, b] = edge.split(",").map(Number);
|
|
229
|
+
const ca = comps.get(a), cb = comps.get(b);
|
|
230
|
+
if (ca === undefined && cb === undefined) {
|
|
231
|
+
comps.set(a, compId);
|
|
232
|
+
comps.set(b, compId);
|
|
233
|
+
compId++;
|
|
234
|
+
}
|
|
235
|
+
else if (ca !== undefined && cb === undefined) {
|
|
236
|
+
comps.set(b, ca);
|
|
237
|
+
}
|
|
238
|
+
else if (cb !== undefined && ca === undefined) {
|
|
239
|
+
comps.set(a, cb);
|
|
240
|
+
}
|
|
241
|
+
// both defined: merge would be needed but keep simple
|
|
242
|
+
}
|
|
243
|
+
const sccCount = compId > 0 ? compId : S;
|
|
244
|
+
// Branching factor
|
|
245
|
+
let totalOut = 0, nonZeroOut = 0;
|
|
246
|
+
for (let i = 0; i < S; i++) {
|
|
247
|
+
let out = 0;
|
|
248
|
+
for (let j = 0; j < S; j++)
|
|
249
|
+
if (matrix[i][j] > 0)
|
|
250
|
+
out++;
|
|
251
|
+
totalOut += out;
|
|
252
|
+
if (out > 0)
|
|
253
|
+
nonZeroOut++;
|
|
254
|
+
}
|
|
255
|
+
const avgBranching = nonZeroOut > 0 ? totalOut / nonZeroOut : 0;
|
|
256
|
+
// Density
|
|
257
|
+
const maxEdges = S * (S - 1);
|
|
258
|
+
const edgeCount = undirected.size;
|
|
259
|
+
const density = maxEdges > 0 ? edgeCount / maxEdges : 0;
|
|
260
|
+
return { isDAG, diameter, sccCount, avgBranching, density };
|
|
261
|
+
}
|
|
262
|
+
// ═══════════════════════════════════════════════════════════════
|
|
263
|
+
// Public API
|
|
264
|
+
// ═══════════════════════════════════════════════════════════════
|
|
265
|
+
/**
|
|
266
|
+
* Infer a state machine from raw call sequences.
|
|
267
|
+
*
|
|
268
|
+
* ZERO dependency on function names. The same sequences with scrambled
|
|
269
|
+
* names produce structurally identical state machines.
|
|
270
|
+
*
|
|
271
|
+
* @param sequences Array of call sequences (e.g., [["open","read","close"],...])
|
|
272
|
+
* @returns InferredStateMachine with opaque state nodes
|
|
273
|
+
*/
|
|
274
|
+
function inferStateMachine(sequences) {
|
|
275
|
+
if (sequences.length === 0) {
|
|
276
|
+
return {
|
|
277
|
+
fnCount: 0, stateCount: 0, states: [],
|
|
278
|
+
stateTransitions: [], isDAG: true, diameter: 0, sccCount: 0, avgBranching: 0,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
// Build function index
|
|
282
|
+
const fnIndex = new Map();
|
|
283
|
+
for (const seq of sequences) {
|
|
284
|
+
for (const fn of seq) {
|
|
285
|
+
if (!fnIndex.has(fn))
|
|
286
|
+
fnIndex.set(fn, fnIndex.size);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Step 1: Role assignment
|
|
290
|
+
const roles = assignRoles(fnIndex, sequences);
|
|
291
|
+
// Step 2: Group into states
|
|
292
|
+
const groups = groupIntoStates(roles);
|
|
293
|
+
// If no clear groups found, create a simple linear state machine
|
|
294
|
+
if (groups.length === 0) {
|
|
295
|
+
return buildFallbackStateMachine(fnIndex, sequences);
|
|
296
|
+
}
|
|
297
|
+
// Step 3: Build state transition matrix
|
|
298
|
+
const stateTransitions = buildStateTransitions(groups, roles);
|
|
299
|
+
// Step 4: Extract stats
|
|
300
|
+
const stats = computeStateGraphStats(stateTransitions, groups);
|
|
301
|
+
// Build InferredState objects
|
|
302
|
+
const states = groups.map((g, i) => {
|
|
303
|
+
let inDeg = 0, outDeg = 0;
|
|
304
|
+
for (let j = 0; j < stateTransitions.length; j++) {
|
|
305
|
+
if (stateTransitions[j][i] > 0)
|
|
306
|
+
inDeg++;
|
|
307
|
+
if (stateTransitions[i][j] > 0)
|
|
308
|
+
outDeg++;
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
id: g.id,
|
|
312
|
+
members: g.members,
|
|
313
|
+
role: inDeg === 0 && outDeg > 0 ? "entry"
|
|
314
|
+
: outDeg === 0 && inDeg > 0 ? "exit"
|
|
315
|
+
: inDeg > 0 && outDeg > 0 ? "bridge"
|
|
316
|
+
: "isolated",
|
|
317
|
+
inDegree: inDeg,
|
|
318
|
+
outDegree: outDeg,
|
|
319
|
+
};
|
|
320
|
+
});
|
|
321
|
+
return {
|
|
322
|
+
fnCount: fnIndex.size,
|
|
323
|
+
stateCount: states.length,
|
|
324
|
+
states,
|
|
325
|
+
stateTransitions,
|
|
326
|
+
...stats,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/** Fallback: build a simple linear chain when grouping fails. */
|
|
330
|
+
function buildFallbackStateMachine(fnIndex, sequences) {
|
|
331
|
+
// Find the longest sequence and use it as a template
|
|
332
|
+
const longest = sequences.reduce((a, b) => a.length >= b.length ? a : b, sequences[0] || []);
|
|
333
|
+
const S = Math.min(longest.length, 10);
|
|
334
|
+
const states = [];
|
|
335
|
+
const fnToState = new Map();
|
|
336
|
+
for (let i = 0; i < S; i++) {
|
|
337
|
+
const fnIdx = fnIndex.get(longest[i]);
|
|
338
|
+
fnToState.set(fnIdx, i);
|
|
339
|
+
states.push({
|
|
340
|
+
id: `F${i}`,
|
|
341
|
+
members: [fnIdx],
|
|
342
|
+
role: i === 0 ? "entry" : i === S - 1 ? "exit" : "bridge",
|
|
343
|
+
inDegree: i > 0 ? 1 : 0,
|
|
344
|
+
outDegree: i < S - 1 ? 1 : 0,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
const matrix = Array.from({ length: S }, () => new Array(S).fill(0));
|
|
348
|
+
for (let i = 0; i < S - 1; i++)
|
|
349
|
+
matrix[i][i + 1] = 1;
|
|
350
|
+
return {
|
|
351
|
+
fnCount: fnIndex.size,
|
|
352
|
+
stateCount: S,
|
|
353
|
+
states,
|
|
354
|
+
stateTransitions: matrix,
|
|
355
|
+
isDAG: true,
|
|
356
|
+
diameter: S - 1,
|
|
357
|
+
sccCount: S,
|
|
358
|
+
avgBranching: S > 0 ? (S - 1) / S : 0,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Extract a fixed-size fingerprint vector from an inferred state machine.
|
|
363
|
+
* All features are in [0,1] where possible.
|
|
364
|
+
*/
|
|
365
|
+
function extractStateFingerprint(sm) {
|
|
366
|
+
const S = sm.stateCount;
|
|
367
|
+
const entryCount = sm.states.filter(s => s.role === "entry").length;
|
|
368
|
+
const exitCount = sm.states.filter(s => s.role === "exit").length;
|
|
369
|
+
const bridgeCount = sm.states.filter(s => s.role === "bridge").length;
|
|
370
|
+
const isolatedCount = sm.states.filter(s => s.role === "isolated").length;
|
|
371
|
+
let totalTransitions = 0;
|
|
372
|
+
let selfLoopCount = 0;
|
|
373
|
+
for (let i = 0; i < S; i++) {
|
|
374
|
+
for (let j = 0; j < S; j++) {
|
|
375
|
+
if (sm.stateTransitions[i]?.[j] > 0) {
|
|
376
|
+
totalTransitions++;
|
|
377
|
+
if (i === j)
|
|
378
|
+
selfLoopCount++;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Degree entropy: Shannon entropy of in/out degree distributions
|
|
383
|
+
const inDegs = new Array(S).fill(0);
|
|
384
|
+
const outDegs = new Array(S).fill(0);
|
|
385
|
+
for (let i = 0; i < S; i++) {
|
|
386
|
+
for (let j = 0; j < S; j++) {
|
|
387
|
+
if (sm.stateTransitions[i]?.[j] > 0) {
|
|
388
|
+
outDegs[i]++;
|
|
389
|
+
inDegs[j]++;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
const entropy = (counts) => {
|
|
394
|
+
const total = counts.reduce((a, b) => a + b, 0);
|
|
395
|
+
if (total === 0)
|
|
396
|
+
return 0;
|
|
397
|
+
return -counts
|
|
398
|
+
.filter(c => c > 0)
|
|
399
|
+
.map(c => c / total)
|
|
400
|
+
.reduce((s, p) => s - p * Math.log2(p), 0);
|
|
401
|
+
};
|
|
402
|
+
// Path length variance: BFS from all entry points
|
|
403
|
+
const pathLengths = [];
|
|
404
|
+
for (const entry of sm.states.filter(s => s.role === "entry")) {
|
|
405
|
+
const entryIdx = sm.states.indexOf(entry);
|
|
406
|
+
const dist = new Array(S).fill(-1);
|
|
407
|
+
const q = [entryIdx];
|
|
408
|
+
dist[entryIdx] = 0;
|
|
409
|
+
while (q.length > 0) {
|
|
410
|
+
const n = q.shift();
|
|
411
|
+
for (let j = 0; j < S; j++) {
|
|
412
|
+
if (sm.stateTransitions[n]?.[j] > 0 && dist[j] === -1) {
|
|
413
|
+
dist[j] = dist[n] + 1;
|
|
414
|
+
q.push(j);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
for (const d of dist)
|
|
419
|
+
if (d > 0)
|
|
420
|
+
pathLengths.push(d);
|
|
421
|
+
}
|
|
422
|
+
const avgPath = pathLengths.length > 0
|
|
423
|
+
? pathLengths.reduce((a, b) => a + b, 0) / pathLengths.length : 0;
|
|
424
|
+
const pathVariance = pathLengths.length > 1
|
|
425
|
+
? pathLengths.reduce((s, p) => s + (p - avgPath) ** 2, 0) / pathLengths.length : 0;
|
|
426
|
+
return {
|
|
427
|
+
stateCount: S,
|
|
428
|
+
fnCount: sm.fnCount,
|
|
429
|
+
transitionCount: totalTransitions,
|
|
430
|
+
entryCount,
|
|
431
|
+
exitCount,
|
|
432
|
+
bridgeCount,
|
|
433
|
+
isolatedCount,
|
|
434
|
+
entryRatio: S > 0 ? entryCount / S : 0,
|
|
435
|
+
exitRatio: S > 0 ? exitCount / S : 0,
|
|
436
|
+
isDAG: sm.isDAG,
|
|
437
|
+
diameter: sm.diameter,
|
|
438
|
+
sccCount: sm.sccCount,
|
|
439
|
+
avgBranching: Math.round(sm.avgBranching * 100) / 100,
|
|
440
|
+
density: Math.round((S > 1 ? totalTransitions / (S * (S - 1)) : 0) * 1000) / 1000,
|
|
441
|
+
selfLoopCount,
|
|
442
|
+
inDegreeEntropy: Math.round(entropy(inDegs) * 1000) / 1000,
|
|
443
|
+
outDegreeEntropy: Math.round(entropy(outDegs) * 1000) / 1000,
|
|
444
|
+
pathVariance: Math.round(pathVariance * 1000) / 1000,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Convert fingerprint to a 14-dim numeric vector for similarity comparison.
|
|
449
|
+
*/
|
|
450
|
+
function stateFingerprintToVector(fp) {
|
|
451
|
+
return [
|
|
452
|
+
fp.stateCount,
|
|
453
|
+
fp.fnCount,
|
|
454
|
+
fp.transitionCount,
|
|
455
|
+
fp.entryCount,
|
|
456
|
+
fp.exitCount,
|
|
457
|
+
fp.bridgeCount,
|
|
458
|
+
fp.isolatedCount,
|
|
459
|
+
fp.entryRatio,
|
|
460
|
+
fp.exitRatio,
|
|
461
|
+
fp.isDAG ? 1 : 0,
|
|
462
|
+
fp.diameter,
|
|
463
|
+
fp.sccCount,
|
|
464
|
+
fp.avgBranching,
|
|
465
|
+
fp.density,
|
|
466
|
+
fp.selfLoopCount,
|
|
467
|
+
fp.inDegreeEntropy,
|
|
468
|
+
fp.outDegreeEntropy,
|
|
469
|
+
fp.pathVariance,
|
|
470
|
+
];
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Cosine similarity between two state fingerprints.
|
|
474
|
+
*/
|
|
475
|
+
function stateFingerprintSimilarity(a, b) {
|
|
476
|
+
const va = stateFingerprintToVector(a);
|
|
477
|
+
const vb = stateFingerprintToVector(b);
|
|
478
|
+
let dot = 0, normA = 0, normB = 0;
|
|
479
|
+
for (let i = 0; i < va.length; i++) {
|
|
480
|
+
dot += va[i] * vb[i];
|
|
481
|
+
normA += va[i] * va[i];
|
|
482
|
+
normB += vb[i] * vb[i];
|
|
483
|
+
}
|
|
484
|
+
if (normA === 0 && normB === 0)
|
|
485
|
+
return 1;
|
|
486
|
+
if (normA === 0 || normB === 0)
|
|
487
|
+
return 0;
|
|
488
|
+
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
489
|
+
}
|
|
490
|
+
// ═══════════════════════════════════════════════════════════════
|
|
491
|
+
// Reporting
|
|
492
|
+
// ═══════════════════════════════════════════════════════════════
|
|
493
|
+
function printInferredStateMachine(sm) {
|
|
494
|
+
console.log(`\n─── Inferred State Machine ───`);
|
|
495
|
+
console.log(` Functions: ${sm.fnCount}`);
|
|
496
|
+
console.log(` Inferred states: ${sm.stateCount}`);
|
|
497
|
+
console.log(` DAG: ${sm.isDAG}`);
|
|
498
|
+
console.log(` Diameter: ${sm.diameter}`);
|
|
499
|
+
console.log(` SCC count: ${sm.sccCount}`);
|
|
500
|
+
if (sm.states.length > 0) {
|
|
501
|
+
console.log(`\n States:`);
|
|
502
|
+
for (const s of sm.states) {
|
|
503
|
+
console.log(` ${s.id} (${s.role}): ${s.members.length} functions, in=${s.inDegree}, out=${s.outDegree}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (sm.stateTransitions.length > 0 && sm.stateTransitions.length <= 10) {
|
|
507
|
+
console.log(`\n State transitions:`);
|
|
508
|
+
for (let i = 0; i < sm.stateTransitions.length; i++) {
|
|
509
|
+
for (let j = 0; j < sm.stateTransitions[i].length; j++) {
|
|
510
|
+
if (sm.stateTransitions[i][j] > 0) {
|
|
511
|
+
console.log(` S${i} → S${j} (${sm.stateTransitions[i][j]} edges)`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|